`ls`をループすると、bashシェルスクリプトが生成されます


93

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

ls -1d */ディレクトリ名のリストを取得する予定です。

回答:


110

@ shawn-j-goffや他の人が示唆したように、グロブが行う場所で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

22
ファイル名にスペースが含まれている場合はどうなりますか?
ダニエルA.ホワイト

4
残念ながら、ダニエルが言ったように、ファイルまたはフォルダーの名前にスペースまたは改行が含まれていると、この回答のコードが壊れます。これは、forループの非常に一般的な誤用と、の出力を解析しようとする典型的な落とし穴を示していますls。@ DanielA.White、あなた言ったようにディレクトリで行動しているので、役に立たなかった(または潜在的に誤解を招く可能性がある)場合、この答えを受け入れないことを検討するかもしれません。Shawn J. Goffの答えは、あなたの問題に対してより堅牢で実用的なソリューションを提供するはずです。
-slhck

4
-∞ あなたが解析してはならないls出力をあなたが出力読むべきではないforループを、あなたが使用する必要があります$()``の代わりに及びその他の引用符を使用し™。@CoverosGeneの意味は確かだと思いますが、これはひどいです。
l0b0 14

1
本当に使用したい場合のより良い代替手段lsls -1 | while read line; do stuff; doneです。少なくとも空白の場合は壊れません。
ejoubaud 14

1
mv -v、あなたは必要ありませんecho "moved $x -> $t"
DmitrySandalov

68

の出力を使用してlsファイル名を取得することはお勧めできません。誤動作や危険なスクリプトにさえつながる可能性があります。ファイル名は除く任意の文字含めることができるからである/null文字を、およびls区切り文字としてこれらの文字のいずれかを使用していないので、ファイル名が空白や改行を持っている場合、あなたがなり、予期しない結果が得られます。

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

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

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

シェルは*/forループが読み取る個別の引数に展開されます。ファイル名にスペース、改行、またはその他の奇妙な文字が含まれている場合でも、for各完全な名前はアトミック単位として表示されます。リストを解析することはありません。

再帰的にサブディレクトリに移動する場合、シェルに拡張グロブ機能(bash's など)がない限り、これは実行されませんglobstar。シェルにこれらの機能がない場合、またはスクリプトが機能することを確認する場合さまざまなシステムで、次のオプションはを使用することfindです。

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

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

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


2
+1これは間違いなく、ファイルリストを生成してコマンドで使用する必要がある場合の方法です。find -execは、単一のコマンドのみを実行できるという制限があります。ループを使用すると、思いのままにパイプできます。
-MaQleod

+1。もっと多くの人が使うことを望みますfind。実行できる魔法があり、認識されている制限さえも-exec回避することができます。この-print0オプションは、との使用にも役立ちますxargs
ゴティ14年

ループオプションは隠しディレクトリを表示しません。検索オプションはシンボリックリンクを表示しません。
jinawee

16

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

 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;

最初の例でのサブシェルの
ジェレミーL

割り当ての後、新しいキャラクターの前にIFSが必要なのはなぜですか?
アンディアイバニーズ

3
ファイル名には改行を含めることもできます。ブレークオン\nは不十分です。の出力の解析を推奨することは決して良い考えではありませんls
ゴティ14年


4

CoverosGeneの回答に追加するために、ディレクトリ名のみをリストする方法を次に示します。

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

1

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

(テストでは、IFSの設定で問題が発生して\nいましたが、ここで推奨されているように、改行のバックスペースに設定できます):

例(必要なls検索パターンが渡されると仮定$1):

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です。


2
ファイル名には改行を含めることもできます。また、ユーザーが自分のファイルに名前を付けることが許可されている場合にファイル名に含まれることがよくあります。ブレークオン\nは不十分です。唯一の有効な解決策は、パス名の展開、またはでforループを使用しますfind
ゴティ14年

ファイル名のリストを転送する唯一の信頼できる方法は、それらをNUL文字で区切ることです。これは、ファイルパスに含まれていない唯一のものであるためです。
glglgl

-3

これは私がそれを行う方法ですが、おそらくより効率的な方法があります。

ls > filelist.txt

while read filename; do echo filename: "$filename"; done < filelist.txt

6
ファイルの代わりにパイプに固執する:>ls | while read i; do echo filename: $i; done
Jeremy L

クール。2つのコマンドの間に$ EDITOR filelist.txtを使用することもできます。コマンドラインよりも簡単なエディターでできることがたくさんあります。ただし、この質問には関係ありません。

あなたのソリューションは、改行や他の派手なものを含むファイル名の問題にまったく対処していません。
glglgl
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.