web-dev-qa-db-ja.com

`ls`をループするとbashシェルスクリプトになる

ディレクトリ名のリストのためにlsを使って何かをし、それぞれをループして何かをするためのテンプレートシェルスクリプトがありますか?

ディレクトリ名のリストを取得するためにls -1d */をやるつもりです。

88
Daniel A. White

@ shawn-j-goffや他の人が示唆しているように、globがするところではlsを使わないように編集しました。

for..do..doneループを使うだけです:

for f in *; do
  echo "File -> $f"
done

**.txt、またはリスト(ファイル、ディレクトリ、その他のもの)を返すグロブ、リストを生成するコマンド($(cat filelist.txt)など)、または実際にはリストに置き換えるコマンドで置き換えることができます。

doループ内では、ドル記号の接頭辞を付けてループ変数を参照するだけです(上記の例では$f)。あなたはそれをechoするか、あなたが望むそれに何か他のことをすることができます。

たとえば、現在のディレクトリにあるすべての.xmlファイルの名前を.txtに変更するには、次のようにします。

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

あるいはもっと良いのは、もしあなたがBashを使っているならば、あなたはサブシェルを生み出すよりむしろBashパラメータ展開を使うことができる:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done
101
CoverosGene

lsの出力を使ってファイル名を取得するのは悪い考えです 。それは誤動作やさらには危険なスクリプトを引き起こす可能性があります。これは、ファイル名に/null文字以外の任意の文字を含めることができ、lsがこれらの文字のどちらも区切り文字として使用しないため、ファイル名にスペースまたは改行がある場合、予期しない結果になります。

ファイルを反復処理する方法は2つあります。ここでは、ファイル名を使用して何かを実行する方法を示すために、単にechoを使用しました。しかし、あなたは何でも使うことができます。

1つはシェルのネイティブグロビング機能を使うことです。

for dir in */; do
  echo "$dir"
done

シェルは*/forループが読み込む別々の引数に展開します。ファイル名にスペース、改行、その他の奇妙な文字が含まれていても、forはそれぞれの完全な名前をアトミックな単位として認識します。リストを解析するわけではありません。

サブディレクトリに再帰的に入りたいのであれば、シェルに拡張グロブ機能(bashglobstarなど)がない限り、これはできません。シェルにこれらの機能がない場合、またはスクリプトを確実に機能させたい場合さまざまなシステムでは、次のオプションはfindを使うことです。

find . -type d -exec echo '{}' \;

ここで、findコマンドはechoを呼び出し、ファイル名の引数を渡します。見つけたファイルごとにこれを1回行います。前の例と同様に、ファイル名のリストの解析はありません。代わりに、ファイル名は引数として完全に送信されます。

-exec引数の構文は少し変わって見えます。 find-execの後の最初の引数を取り、それを実行するプログラムとして扱い、それ以降のすべての引数はそのプログラムに渡す引数として取ります。 -execが見る必要がある2つの特別な引数があります。最初のものは{}です。この引数は、findの前の部分が生成するファイル名に置き換えられます。 2つ目は;で、これはfindにこれがプログラムに渡す引数のリストの最後であることを知らせます。 findはこれを必要とします。これはfindを対象とし、execされたプログラムを対象としないより多くの引数を続けることができるからです。 \の理由は、シェルが;を特別に扱うからです。それはコマンドの終わりを表します。したがって、シェルがそれをfindへの引数として渡すのではなく、それをエスケープする必要があります。シェルが特別に扱わないようにするもう1つの方法は、引用符で囲むことです。';'は、この目的のために\;と同じように機能します。

64
Shawn J. Goff

スペースが含まれているファイルの場合は、必ず次のように変数を引用符で囲む必要があります。

 for i in $(ls); do echo "$i"; done; 

あるいは、入力フィールド分離文字(IFS)環境変数を変更することもできます。

 IFS=$'\n';for file in $(ls); do echo $i; done

最後に、あなたがしていることによっては、lsも必要ないかもしれません。

 for i in *; do echo "$i"; done;
14
john

GNU Parallel http://www.gnu.org/software/parallel/ がインストールされている場合は、これを実行できます。

ls | parallel echo {} is in this dir

すべての.txtの名前を.xmlに変更するには

ls *.txt | parallel mv {} {.}.xml

詳しくはGNU Parallelの紹介ビデオをご覧ください。 http://www.youtube.com/watch?v=OpaiGYxkSuQ

5
Ole Tange

CoverosGeneの答えに加えて、ディレクトリ名だけをリストする方法があります。

for f in */; do
  echo "Directory -> $f"
done
4
n3o

IFSを改行に設定してからlsの出力を配列にキャプチャしませんか? IFSを改行に設定すると、ファイル名におかしな文字が含まれる問題を解決するはずです。 lsを使用すると、ソート機能が組み込まれているので、Niceにすることができます。

(テストでは、IFSを\nに設定するのに問題がありましたが、改行のバックスペースに設定すると、ここで他の場所で示唆されているように動作します)

例えば。 ($1に渡される必要なls検索パターンを仮定します):

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

FILES=($(/bin/ls "$1"))

for AFILE in ${FILES[@]}
do
    ... do something with a file ...
done

IFS=$SAVEIFS

これはOS X上では特に便利です。例えば、作成日順にソートされたファイルのリスト(最も古いものから新しいものへ)をキャプチャする場合、lsコマンドはls -t -U -rです。

1
jetset