web-dev-qa-db-ja.com

読み取り行を使用して変数を反復処理する

次のファイルがあります。

$ ls *.png | egrep -i "am|pm"
output-0 11.42.30 AM.png
output-0 5.10.12 PM.png
....

それらを削除したいのですが、このエラーが発生します:

$ ls *.png | egrep -i "am|pm" | while read line; do rm "$line"; done
rm: cannot remove ''$'\033''[0m'$'\033''[01;35moutput-0 11.42.30 AM.png'$'\033''[0m': No such file or directory
rm: cannot remove ''$'\033''[01;35moutput-0 5.10.12 PM.png'$'\033''[0m': No such file or directory
rm: cannot remove ''$'\033''[01;35moutput-1 11.42.30 AM.png'$'\033''[0m': No such file or directory

コードの問題は何ですか?

3
user10726006

lsは、スクリプトおよびコマンドラインでは危険です。 1つの問題は、カラー出力用のANSIエスケープシーケンスである@PerlDuckのコメントに記載されていることです。 findを使用した別のアプローチをお勧めします

  • テストファイルを作成する

    touch 'output-0 11.42.30 AM.png' 'output-0 5.10.12 PM.png' asdf.png
    
  • findcommand行が、期待するファイルを見つけることを確認します

    find . -type f -iname "*[ap]m*.png"
    

    そしてそれを行う

    find . -type f -iname "*[ap]m*.png" -delete
    

    サブディレクトリを検索したくない場合は、-maxdepth 1を追加してください

    find . -maxdepth 1 -type f -iname "*[ap]m*.png" -delete
    
  • 結果を確認する

    ls
    

    名前にam、pm、AM、PMが含まれるファイルは削除する必要がありますが、asdf.pngは削除されません。

9
sudodus

本当にwhile readループを使用したい場合は、シェルグロブとprintfを使用します。

printf '%s\0' *.png | grep -zEi 'am|pm' | while IFS= read -r -d '' line; do echo "$line"; done

ただし、bashを取得して、直接削除するファイルのリストに展開するには、grepおよびwhileにパイプする必要はありません。

  1. 単純なシェルグロブの使用

    rm -- *[ap]m.png *[AP]M.png
    

    または-.PNG拡張子も一致することを気にしない場合-nocaseglobオプションを使用する

    shopt -s nocaseglob
    rm -- *[ap]m.png
    
  2. 拡張グロブを使用して

    shopt -s extglob
    rm -- *@(AM|PM).png *@(am|pm).png
    

または

    shopt -s extglob nocaseglob
    rm -- *@(am|pm).png

--は、ハイフンで始まるファイル名がある場合に備えて、オプションの終わりを示します-必要に応じて、代わりに./*(am|pm)のような明示的なディレクトリプレフィックスを持つパターンを使用できます。

削除する前にファイル名をインタラクティブに確認する場合は、-iまたは-Iオプションを追加します。

6
steeldriver