bashでスクリプトが実行されると、変数の内容をファイルにダンプします。

bashでスクリプトが実行されると、変数の内容をファイルにダンプします。

私のディレクトリにはたくさんのファイルがあります:

$ ls
file000001
file000002
# ... truncated ...
file999999

私はこのようなファイルのmd5sumを計算し、最終的にファイルにダンプします。

hashes=''
for file in $(ls); do
  hashes+=$(md5sum $file)
  hashes+="\n"
done

echo "$hashes" > hashes.txt

さて、ループ内でスクリプトを実行しながらCtrl + Cを押し、内容をhashes.txtファイルにダンプしたいとfor思います。hashes可能ですか?

(はい、各ファイルのmd5sumを計算するたびにmd5sumを追加できますが、hahes.txtそうします(上記のように)。)

上記のサンプルコードはひどいです。私は実際にmd5sumを例として使用しています。私の質問の目的は、実際にCtrl + Cを操作する方法を見つけることです。

答え1

trap ctrl_c INT

ctrl_c() {
    echo "$hashes" > hashes.txt
    exit 0
}

lsところで、私はファイルのリストを取得するために使用しませんがfor file in *; do

答え2

trap break INTループ前:

hashes=
trap break INT
for file in *; do
  hashes+=$(md5sum -- "$file")$'\n'
done
trap - INT

echo "$hashes" > hashes.txt

$(ls)あなたのスクリプト(、)の疑わしい部分を修正しました"\n"

mkshFWIW、これらの「非ローカル割り込み」はまたはでは動作しませんが、およびyashでは動作します。bashdashzshksh93

答え3

lsの出力を他のプログラムの入力として使用しないでください。 (信頼性や安全の概念を含む「動作」の定義について)うまくいかず、次の問題があります。いいえシェルのワイルドカード(ワイルドカード拡張など)は誤用で実行したいことを行うため、これを行う必要がありますlsなぜいいえ解析ls(および実行方法)?

同様の作業を行う必要がある場合は、代わりにfind ... -exec {} +orを使用してください。どちらかが実際に動作し、ファイル名にどの文字があっても安全に動作します。find ... -print0 | xargs -0r ...ls

ループforは次のように書く必要があります。

for file in *; do
  ...
done

ループ内の各ループに対して異なる操作を実行しforたい場合を除き、ループは必要ありません。$file

以下は、forループ操作を実行し、出力を名前付きmd5sum配列に保存してhashes印刷するいくつかの代替方法です。

  1. 使用printf:

    $ hashes=( $( md5sum * ) )
    $ printf '%s  %s\n' "${hashes[@]}" > hashes.txt
    $ cat hashes.txt
    b026324c6904b2a9cb4b88d6d61c81d1  file.1
    26ab0db90d72e28ad0ba1e22ee510510  file.2
    6d7fce9fee471194aa8b5b6e47267f03  file.3
    

    2つのフォーマット文字列は%s2つのスペースで区切られていますprintf。これはそれ自体と同じ出力を生成しますmd5sum

    次のように、アイテムが配列にどのように保存されるかを確認できますtypeset -p

    $ typeset -p hashes
    declare -a hashes=([0]="b026324c6904b2a9cb4b88d6d61c81d1" [1]="file.1"
    [2]="26ab0db90d72e28ad0ba1e22ee510510" [3]="file.2"
    [4]="6d7fce9fee471194aa8b5b6e47267f03" [5]="file.3")
    

    シェルは出力をmd5sum単語($IFSシェル変数の値として定義)に分割し、各「単語」を配列の別々の要素に入れます。

  2. 使用tee:

    $ hashes=( $( md5sum * | tee hashes.txt) )
    $ cat hashes.txt
    b026324c6904b2a9cb4b88d6d61c81d1  file.1
    26ab0db90d72e28ad0ba1e22ee510510  file.2
    6d7fce9fee471194aa8b5b6e47267f03  file.3
    

これを知ると、Lase Kliemannのトラップに対する答えは次のように書くことができます。

trap ctrl_c INT

ctrl_c() {
    printf '%s  %s\n' "${hashes[@]}" > hashes.txt
    exit 0
}

しかし、ここでは罠すら必要ありません。 md5sumwithはあなたがtee望むことを行います。つまり、$hashesANDhashes.txtファイルを同時に埋めます。

または配列を使用したくない場合:

$ for file in *; do
    hashes+="$(md5sum "$file" | tee -a hashes.txt)"$'\n'
  done

$ echo "$hashes"
b026324c6904b2a9cb4b88d6d61c81d1  file.1
26ab0db90d72e28ad0ba1e22ee510510  file.2
6d7fce9fee471194aa8b5b6e47267f03  file.3

ここでは、ループ内で実行され、繰り返しごとにファイルを上書きしたくないので、tee' -a--append)オプションを使用します。md5sumhashes.txt

注:文字列の末尾には常に空白行が追加されているため、$hashes行数は1ずつ増えます。配列を使用すると、これは発生しません。

関連情報