Bash 将文件中特定位置的值替换为另一个文件中的另一个值

Bash 将文件中特定位置的值替换为另一个文件中的另一个值,bash,sed,Bash,Sed,我们有两个文件,一个是从61到67位用零填充的值,另一个是从1到7位的计数器。 我们需要将此计数器从第二个文件替换为第一个文件。 替换文件中的字符串可以通过 sed -i 's/old-text/new-text/g' input.txt 但对于特定的位置,如何替换和从另一个文件中获取位置,可能有人会提供帮助 需要更改的文件1,例如: 计数器编号文件,例如: 已尝试的代码: echo File Name: "$1" echo Number File Name: "$2" filename="$

我们有两个文件,一个是从61到67位用零填充的值,另一个是从1到7位的计数器。 我们需要将此计数器从第二个文件替换为第一个文件。 替换文件中的字符串可以通过

sed -i 's/old-text/new-text/g' input.txt
但对于特定的位置,如何替换和从另一个文件中获取位置,可能有人会提供帮助

需要更改的文件1,例如:

计数器编号文件,例如:

已尝试的代码:

echo File Name: "$1"
echo Number File Name: "$2"
filename="$1"
numberfilename="$2"
echo $numberfilename
if [ ! -f "$numberfilename" ]
then
    echo "$0: File '${numberfilename}' not found."
elif [ ! -f "$filename" ]
then
    echo "$0: File '${filename}' not found."
else
    IFS=$'\n' read -d '' -r -a numbers < $numberfilename

    IFS=$'\n' read -d '' -r -a records < $filename

    i=0
    while read record; do
        sed 's/^\(.\{61\}\)0000000/\${numbers[i]:0:7}/'
        echo "${record}"
    ((i=i+1))
    done < $filename

  fi
awk'NR==FNR{counter[NR]=substr($0,1,6);next}
{printf(“%62.62s%06i%s\n”,$0,计数器[++i],substr($0,68))}'“$numberfilename”“$filename”
更一般地说,您不希望在阅读时使用
;执行
。。。当
sed
和Awk已经提供了相同的功能,并且通常运行得更快时(同时避免当前代码的脆性问题;阅读
read-r
等)

NR==FNR
Awk习惯用法是将第一个文件读入内存,然后处理第二个文件的常用方法(读取第一个文件时条件为true,处理第二个文件时条件为false,因此在
下一个
之后就陷入了错误)

更详细地说,我们将数字收集到一个数组
计数器
,然后在处理第二个文件时从数组中注入值


演示:

这是我的工作解决方案,但不是优化的:

#!/bin/bash
echo Record File Name: "$1"
echo Numbers File Name: "$2"
recordFileName="$1"
NumberFileName="$2"

if [ ! -f "$NumberFileName" ] 
then
    echo "$0: File '${NumberFileName}' not found."
elif [ ! -f "$recordFileName" ] 
then
    echo "$0: File '${recordFileName}' not found."
else
#Fetching numbers from numbers file
    IFS=$'\n' read -d "" -r -a numbers < "$NumberFileName"
    echo "${#numbers[@]} numbers fetched"
    IFS=$'\n' read -d '' -r -a records < "$recordFileName"
    echo "${#records[@]} Records fetched"

    if [ ${#records[@]} -ge ${#records[@]} ]
    then

    #Counter initialized
        i=0
    #Clearing the final file if already present
       > "${recordFileName%.*}_final.dat"

    #Loop to replace the characters to replace the numbers fetched
        while read -r record; do
            if [ "${record:0:1}" = 2 ] 
            then
                var1=${record:0:61}
                var2=${record:68}   
                var3=${numbers[i]:0:7}
                echo "$var1$var3$var2" >> "${recordFileName%.*}_final.dat"
            else
                var4=${record:0}
                echo "$var4" >> "${recordFileName%.*}_final.dat"
            fi
            ((i = i + 1))
        done < "$recordFileName"
        echo "Numbers replaced to final file ${recordFileName%.*}_final.dat"
    else     
        echo "Numbers Count less than Records count."
    fi
fi
#/bin/bash
回显记录文件名:“$1”
回音号码文件名:“$2”
recordFileName=“$1”
NumberFileName=“$2”
如果[!-f“$NumberFileName”]
然后
回显“$0:找不到文件“${NumberFileName}”
elif[!-f“$recordFileName”]
然后
回显“$0:找不到文件“${recordFileName}”
其他的
#从数字文件获取数字
IFS=$'\n'读取-d'-r-a数字<“$NumberFileName”
回显“${#数字[@]}获取的数字”
IFS=$'\n'read-d'-r-a记录<“$recordFileName”
回显“${#记录[@]}已获取的记录”
如果[${记录[@]}-ge${记录[@]}]
然后
#计数器初始化
i=0
#清除最终文件(如果已存在)
>“${recordFileName%.*}\u final.dat”
#循环以替换字符以替换获取的数字
读写记录时;做
如果[“${record:0:1}”=2]
然后
var1=${record:0:61}
var2=${record:68}
var3=${numbers[i]:0:7}
echo“$var1$var3$var2”>>“${recordFileName%.*}\u final.dat”
其他的
var4=${记录:0}
echo“$var4”>>“${recordFileName%.*}\u final.dat”
fi
((i=i+1))
完成<“$recordFileName”
echo“数字替换为最终文件${recordFileName%.*}\u final.dat”
其他的
echo“数字计数小于记录计数。”
fi
fi
这可能适合您(GNU
sed
):

将整个文件2复制到文件1第一行的保留空间中

将保留空间(file2)追加到file1的每一行

使用regexp和back引用,将位置61到67替换为保留空间(file2)的前7个字符

删除保留空间的第一行(文件2),打印结果并重复

或者,使用
粘贴

paste file1 file2 | sed -E 's/^(.{60}).{7}(.*)\t(.{7}).*/\1\3\2/'

注意:
paste
使用选项卡作为默认分隔符。

欢迎使用堆栈溢出。这也是一个面向专业和热情程序员的问答页面。请在问题中添加您自己的代码。您至少需要展示您自己为解决此问题所做的研究。请在您的问题中添加示例输入(无描述、无图像、无链接)和该示例输入的所需输出(无评论)@Cyrus希望这能解释我要做什么。谢谢@tripleee,但我不太擅长编写脚本,无法理解你是如何从数字文件中获取数字的。我尝试将其添加到我的脚本中,它打印出了输入文件,没有任何更改。我也附上了我的解决方案。代码在本地为我工作。一个常见的初学者问题是,如果您的数据包含DOS回车符,尽管我猜这不是问题所在。更新了一个小补丁、解释和演示链接。
++I
可能只是
FNR
。顺便说一句,我想你的打印语句应该是
printf“%s%06d%s\n”、substr($0,1,60)、counter[FNR]、substr($0,68)
你应该复制/粘贴到它并修复它告诉你的错误。另请参阅了解为什么不使用此方法的原因,以及正确的方法。感谢@EdMorton的shellchecker将解决问题。但我所做的循环只是为了让我发送的用户更容易理解,并在最后轻松地进行更改。50行(或其他)复杂的shell脚本(顺便说一句,其中包含shellcheck不会告诉您的bug)不可能比同等脚本更容易理解或修改(但更健壮、更便携、更高效)几行awk。
paste
版本更干净,可以提供更好的引导答案。
cat
版本效率低下,特别是如果文件2很大,因为
x
G
总是来回交换一个大的(逐渐减小的)缓冲区。
10300109300109300109300009806200000012345678912345021 00000011122230123456789                                                                   
23000000201093001093011100000485806212345678912345021 00000012245550123456789                                     
23000000601093001093011100002225409112345678912345021 00000012223320123456789 
10300109300109300109300009806200000012345678912345021 00000012245550123456789
#!/bin/bash
echo Record File Name: "$1"
echo Numbers File Name: "$2"
recordFileName="$1"
NumberFileName="$2"

if [ ! -f "$NumberFileName" ] 
then
    echo "$0: File '${NumberFileName}' not found."
elif [ ! -f "$recordFileName" ] 
then
    echo "$0: File '${recordFileName}' not found."
else
#Fetching numbers from numbers file
    IFS=$'\n' read -d "" -r -a numbers < "$NumberFileName"
    echo "${#numbers[@]} numbers fetched"
    IFS=$'\n' read -d '' -r -a records < "$recordFileName"
    echo "${#records[@]} Records fetched"

    if [ ${#records[@]} -ge ${#records[@]} ]
    then

    #Counter initialized
        i=0
    #Clearing the final file if already present
       > "${recordFileName%.*}_final.dat"

    #Loop to replace the characters to replace the numbers fetched
        while read -r record; do
            if [ "${record:0:1}" = 2 ] 
            then
                var1=${record:0:61}
                var2=${record:68}   
                var3=${numbers[i]:0:7}
                echo "$var1$var3$var2" >> "${recordFileName%.*}_final.dat"
            else
                var4=${record:0}
                echo "$var4" >> "${recordFileName%.*}_final.dat"
            fi
            ((i = i + 1))
        done < "$recordFileName"
        echo "Numbers replaced to final file ${recordFileName%.*}_final.dat"
    else     
        echo "Numbers Count less than Records count."
    fi
fi
sed -E '1{x;s/^/cat file2/e;x};G;s/^(.{60}).{7}([^\n]*)\n(.{7}).*/\1\3\2/;x;s/^[^\n]*\n//;x' file1
paste file1 file2 | sed -E 's/^(.{60}).{7}(.*)\t(.{7}).*/\1\3\2/'