ファイルから読み込まれた変数はソートされておらず、追加の行があります。

ファイルから読み込まれた変数はソートされておらず、追加の行があります。
#!/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 コマンドを実行すると、ファイル全体が読み取られ、編集ルールが適用されます。すべて行(規則に適用場所を制限する「アドレス」行がない場合)したがって、あなたの例では、3つの空行のみを持つファイルで始まり、そのファイルを実行すると、sed -i -e "i\input $fieldname "「enter abc」などのファイルの前に行が追加されます。これらの3行はそれぞれ。したがって、次のファイルがあります。

input abc 

input abc 

input abc 

(安全ではありませんが、最後に空白があります。)彼らは、あなたが実行するとsed -i -e 's/^/ /'前に4つのスペースを追加します。すべて行(空行を含む):

    input abc 

    input abc 

    input abc 

その後、実行するとsed -i '/^[[:space:]]*$/d'実際に期待どおりに実行されます。空白のみの行を削除し、次のままにします。

    input abc 
    input abc 
    input abc 

その後、ループの次の反復でrunを実行すると、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 

...など。これはあなたがしたいことをするわけではありません。別の言葉

あなたがやろうとしていることを私が理解しているなら、sedそれは実際に仕事に適したツールではありません。既存のファイルを編集しようとするのではなく、新しいファイルを作成して1行ずつ追加したいようです。次のように簡単にこれを行うことができます。

: >"$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

「行番号...」エントリを印刷する必要がない場合、またはstdoutの代わりにstderrに送信できる場合(実際のエラーでなくても通常ステータス情報を送信する必要がある場所)、次のことができます。はるかに簡単です。

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この場合、変数の値を単語に分割するためにシェルに依存するためです...これは特に安全ではありません。本当に繰り返したいですか?性格入力ファイルにあるか、ループが必要です。ワイヤー代わりに?行が必要な場合は、その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:「ファイル(データストリーム、変数)を1行ずつ(および/またはフィールドごとに)どのように読みますか?」より多くの情報を知りたいです。

@Kusalanandaが(現在削除された)コメントで指摘したように、これを1行ずつ実行し、「行番号...」出力がまったく必要ない場合、シェルsedループでは使用できません。自分自身を取得し、sed入力ファイルをスキャンしてinput各行に「」を追加します。

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

とにかく、現在$ FILE1を文字通り「FILE2」というファイルにコピーし、それをvalue変数という名前で読み込んでいます。これらのいずれも、元のファイルから直接読み取る必要はありません。

また、注:すべて大文字名の代わりに小文字または大文字と小文字の混合変数名を使用してください。シェルやその他のツールに特別な意味を持つ大文字の名前がたくさんあり、そのうちの1つを誤って使用すると奇妙なことが発生する可能性があります。

関連情報