从文件中读取的变量未对齐并且有多余的行

从文件中读取的变量未对齐并且有多余的行
#!/bin/bash

FILES=/tmp/files.txt
FIELDNAME=/tmp/fieldname.txt

num=$(wc -l < $FIELDNAME)

#to read fieldname.txt content
FILE1=$1
cat $FILE1 > FILE2
value=$(<FILE2)

#to create empty lines
yes '' | sed $num\q >> $FILES

#to add fieldname content into files.txt
I=0
for fieldname in $value
do
    echo "Line number $((I++)) --> $fieldname"
    sed -i -e "i\input $fieldname " $FILES
    sed -i -e 's/^/    /' $FILES
    #to remove empty lines
    sed -i '/^[[:space:]]*$/d' $FILES
done

sed -i '/^[[:space:]]*$/d' $FILES

我的脚本名称是 script.sh,这就是我调用脚本的方式:

./script.sh fieldname.txt

预期结果是:

    input abc
    input def
    input ghi

但我得到的输出不对齐并且超过 3 行,如下所示:

    input ghi
        input def
    input ghi
            input abc
    input ghi
        input def
    input ghi
            input abc
    input ghi
        input def
    input ghi
            input abc

答案1

您似乎误解了它的sed工作原理——当对文件运行 sed 命令时,它会读取整个文件,并将编辑规则应用于每一个行(除非规则有一个“地址”行来限制它们的应用位置)。因此,在您的示例中,您从一个只有三个空行的文件开始,然后sed -i -e "i\input $fieldname "在其上运行,它会在前面添加一行,例如“输入 abc ”这三行中的每一行。因此,您有一个如下所示的文件:

input abc 

input abc 

input abc 

(你看不到它,但末尾有一个空行。)他们,你运行sed -i -e 's/^/ /',它会在前面添加四个空格每一个行(包括空白行):

    input abc 

    input abc 

    input abc 

然后,你运行sed -i '/^[[:space:]]*$/d',它实际上做了你所期望的事情——它删除了除了空格之外什么都没有的行,留下:

    input abc 
    input abc 
    input abc 

然后,在循环的下一次迭代中,运行sed -i -e "i\input def ",这再次将该新行放在每个现有行之前:

input def 
    input abc 
input def 
    input abc 
input def 
    input abc 

然后sed -i -e 's/^/ /'再次向每行添加 4 个空格(包括已经有空格的行):

    input def 
        input abc 
    input def 
        input abc 
    input def 
        input abc 

...ETC。这不是做你想做的事根本不

如果我明白你想要做什么,sed那确实是不适合这项工作的工具。您似乎想要创建一个新文件,并逐行添加到其中,而不是尝试编辑现有文件。你可以很容易地做到这一点,如下所示:

: >"$FILES"    # This empties the file (in case there's something there from last run)

I=0
for fieldname in $value
do
    echo "Line number $((I++)) --> $fieldname"
    echo "    input $fieldname" >>"$FILES"    # Append a line to the end of the file
done

如果您不需要打印“行号...”内容,或者可以将其发送到标准错误输出而不是标准输出(通常是您应该发送状态信息的位置,即使它不是实际错误),你可以让它变得更简单:

I=0
for fieldname in $value
do
    echo "Line number $((I++)) --> $fieldname" >&2    # The >&2 redirects to standard error
    echo "    input $fieldname"
done >"$FILES"    # Just send *all* standard output from the loop into the file

在这两种情况下,您都不需要num或 的输出来yes预加载文件。

这里还有很多其他看起来不好的做法。一方面,变量引用几乎总是应该用双引号引起来,就像"$FILES"我上面的例子一样。这可以防止它们被意外地分割成多个“单词”,和/或扩展为文件名通配符。我建议shellcheck.net指出这样的常见错误。

请注意,我没有使用$value( for fieldname in $value) 执行此操作,因为在这种情况下,您依赖于 shell 将变量的值拆分为单词...这不是特别安全。你真的想循环吗在输入文件中,或者你想循环线反而?如果您想要线条,请不要使用该for ... in构造,请使用read循环:

I=0
while read fieldname
do
    echo "Line number $((I++)) --> $fieldname" >&2
    echo "    input $fieldname"
done <"$FILE1" >"$FILES"    # Read input from $FILE1, write output to $FILES

BashFAQ #001:“如何逐行(和/或逐字段)读取文件(数据流、变量)?”了解更多信息。

正如 @Kusalananda 在(现已删除)评论中指出的那样,如果您正在逐行执行此操作并且根本不需要“行号...”输出,则可以使用sed.但不是在 shell 循环中,只是让sed自己扫描输入文件,input在每一行中添加“”:

sed 's/^/    input /' "$FILE1" >"$FILES"

无论如何,您当前正在将 $FILE1 复制到一个字面名为“FILE2”的文件,然后将其读入名为value... 的变量中,而这些都不是必需的;无论你做什么都应该直接从原始文件中读取。

另请注意:使用小写或混合大小写的变量名称,而不是全部大写的名称。有一堆全大写名称对 shell 和/或其他工具具有特殊含义,如果您错误地使用其中一个,可能会发生奇怪的事情。

相关内容