コマンド出力をパラメータリストとしてスクリプトに渡すにはどうすればよいですか?

コマンド出力をパラメータリストとしてスクリプトに渡すにはどうすればよいですか?

現在のディレクトリにあるすべてのファイルの先頭にコンテンツを追加したいと思います。現在の作業ディレクトリからすべてのファイル名を一度に取得し、引数として渡す方法を学ぶ必要があります。各ファイルをスクリプトの引数として手動で指定することで、これを行う必要はありません。私はこの新しい知識を追加のスクリプトに使用したいと思います。私は側面や他の解決策は必要ありません。 command(ls)出力を取得してパラメータに渡す方法を学ぶ必要があります。私はこれを試しましたが失敗しました。

./file-edit.sh $(ls)
./file-edit.sh `ls`

これは私の作業スクリプトです。

#!/bin/bash
lineno=2            # Append from line 2 
BAD_ARGS=85         # Error code

string0="###################################################################"
string2="#Description    :"
string3="#Date           :`date +%d-%B-%Y`"
string4="#Author         :Milos Stojanovic"
string5="#Contact:       :https://www.linkedin.com/in/xxx/"
string6="###################################################################"
# Can be any other strings

if [ ! -f $1 ]; then
        echo "Please specify at least 1 filename as an argument!"
        exit $BAD_ARGS
fi

until [ -z  "$1" ]
do
        string1="#Script Name    :$1"
        file=$1;
        sed -i ""$lineno"i $string0\n$string1\n$string2\n$string3\n$string4\n$string5\n$string6" $file
        echo "File "\'$file\'" altered at line number: $lineno"
        shift 1
done

exit 0

答え1

スクリプトで各パラメータを個別に処理するには、次のようにスクリプトでループを使用します。

for pathname do

    # Insert code here that uses "$pathname".
    # You will not need to shift arguments.

done

現在のディレクトリのすべての名前でスクリプトを呼び出すには、次のようにします。

./script.sh ./*

シェルは、./*パターンを現在のディレクトリのすべての名前を含むリストに展開します。

使用に比べて利点は、有効な文字(スペース、タブ、改行を含む)を含むファイル名をls使用しないため、処理できることです。ls

あるいは、ファイル名ワイルドカードをスクリプト自体に移動することもできます(コマンドラインでスクリプトに引数を提供する必要はありません)。

for pathname in ./*; do
    # Insert code here that uses "$pathname".
done

スクリプトが通常のファイルを参照する名前と非伝統的なファイル(ディレクトリなど)を参照する名前とを区別する必要がある場合は、-fループでテストを使用してください。

# skip non-regular files
[ ! -f "$pathname" ] && continue

これにより、通常のファイルへのシンボリックリンクが可能になります。これもスキップするには、テストを次に変更してください。

if [ ! -f "$pathname" ] || [ -L "$pathname" ]; then
    # Skip non-regular files and symbolic links.
    continue
fi

その他の提案:

文字列に改行文字を含む複数行文字列を変数に割り当てることができます。

boilerplate='Hello and welcome!
This is my program.
Enjoy'

テキスト文書の2行目の後にテキストを挿入するにはsed-iここでは内部編集に使用されます)を使用します。

printf '%s\n' "$boilerplate" | sed -i '2r /dev/stdin' somefile

またはこちらのドキュメントを使用して

sed -i '2r /dev/stdin' somefile <<END_BOILERPLATE
Hello and welcome!
This is my program.
Enjoy
END_BOILERPLATE

答え2

他の答えは正確に何を間違っているかを扱っていないので

私はこれを試しましたが失敗しました。

./file-edit.sh $(ls)

あなたのスクリプトを試した結果、次のような結果が得られました。

$ ./file-edit.sh $(ls)
Please specify at least 1 filename as an argument!

出力を確認するときに、ディレクトリがls最初にリストされていることを確認しました。サブディレクトリがないディレクトリに移動すると、次の結果が表示されます。

$ ./file-edit.sh $(ls)
File 'a' altered at line number: 2
File 'b' altered at line number: 2
File 'c' altered at line number: 2 

したがって、主張を確認する方法が問題です。-f通常のファイルでのみ成功します。最初の引数にディレクトリが指定されたことを確認し、ユーザーがファイル名引数を指定したにもかかわらず指定されなかったというエラーを表示します。

少なくとも1つの引数が指定されていることを確認するには、$#それを使用して指定された引数の数だけ拡張します。

if [ "$#" -eq 0 ]; then
  echo "Please specify at least 1 filename as an argument!"
  ...

通常のファイルであることを確認するには、ループで実行し、最初のファイルだけでなく各ファイルも確認します。

until [ -z  "$1" ]
do
        string1="#Script Name    :$1"
        file=$1;
        if [ -f "$file" ]; then
                sed -i "${lineno}i $string0\n$string1\n$string2\n$string3\n$string4\n$string5\n$string6" "$file"
                echo "File '$file' altered at line number: $lineno"
        fi
        shift 1
done

このコードは、非伝統的なファイルパラメータを無視したい場合のためのものですが、必要に応じて非伝統的なファイルが表示されたときに終了するように簡単に変更できます。

ただし、lsそのような出力を使用することはお勧めできません。ファイル名にスペースが含まれている可能性があります。 bashはすべてをスペースに分割します。ファイルがある場合、foo bar.txtbashはファイルを分割し、スクリプトはファイルfoobar.txt

あなたの質問についてのコメントを見ると、誰かが代わりにglobを使うように提案したようです。私はそれがうまくいかないというあなたのポイントをよく理解していません。./file-edit.sh *私にとってうまくいきます。

答え3

私は次のようなものを選択します。

find <dir> -type f -print0 | xargs -0 sed -i '...'

全体のサイクルではなくuntil。それも速くなければなりません。

代替案を投稿する理由:

  • ls出力を解析しないでください。(600+投票)

  • 私は私のスクリプトで1行の速いタスクを実行するために自分の(おそらくバグがある)複数行のループ構成をハックするよりもむしろシステムメカニズムを使用したいと思います。

  • ほとんどの自己構築構造には、「空白のファイル」の問題が発生するか、パラメータの長さの問題があります。

関連情報