ループ変数の出力は予想とは異なる値を示します

ループ変数の出力は予想とは異なる値を示します

現在の作業ディレクトリに と の 2 つのフォルダがあるとします。back/それぞれfront/のフォルダ内に入って、いくつかの操作を実行しようとしています。以下の簡単なスクリプトから:

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

次のような出力を期待していました:

back
front

しかし、私が得ているのは次の通りです:

back  front
back  front

理解できません。その結果、cdフォルダにアクセスしようとすると、次のようになります。

for dir in */ ; do
    echo "$(dir)"
    cd $(dir)
    echo "$(pwd)"
    cd ..
done

次のような結果になります:

back  front
/path/to/back
back  front
/path/to/back

つまり、私は決して入力しませんfront/。誰かが私が何を間違っているのか理解するのを手伝ってくれませんか?

答え1

POSIXのようなシェルでは、$(cmd)構文は次のようになります。コマンド置換は、末尾の改行文字$(cmd)を除いた出力に展開されます。cmd

代わりにあなたが望むのはパラメータ拡張:$dirまたは${dir}(ここではパラメータはdir 変数)。

pwd( print working irectory) は、現在の作業ディレクトリへのパスを含む ¹ 特殊変数dの内容を出力するコマンドです。したがって、改行文字で終わらない限り、と同じものに展開されます。$PWD$(pwd)$PWD$PWD

あなたが望んでいるのは:

for dir in */; do 
  (
    printf '%s\n' "$dir"
    cd -- "$dir" &&
      printf '%s\n' "$PWD"
  )
done

または:

for dir in */; do 
  (
    printf '%s\n' "$dir"
    cd -- "$dir" &&
      pwd
  )
done

また覚えておいてください

  • echo任意のデータを出力するために使用することはできません。printf代わりに
  • 通常は、コマンドの終了ステータスを確認する必要があります。ここでの出力を確認しないと、最初のコマンドが失敗した場合にcd次のコマンドをcd ..実行すると間違った場所に行くことになりますcd
  • を使用するよりも、cdサブシェルで実行した方がよいでしょう。後者では必ずしも最初の場所に戻れるとは限らないからです(シンボリックリンクが関係している場合や、パスコンポーネントの一部が区間内で名前変更されている場合)。(...)cd ..
  • ---非オプションが先頭にないことを保証できない場合(または+一部のコマンドでは問題になる可能性がある場合)、オプションと非オプションを区別するために使用する必要があります。
  • POSIX ライクなシェルでは、パラメータ展開とコマンド置換 (および算術展開) は引用符で囲む必要があります。そうしないと、暗黙の split+glob が実行され (zsh を除く)、通常は望ましくありません。

また、bash (および zsh 以外の他の POSIX シェル) では、glob がどのファイルにも一致しない場合は展開されないことに注意してください。

したがって、現在の作業ディレクトリに、隠しファイルでないファイルが含まれていない場合、そのファイルの種類はディレクトリ、 glob パターンは空のリストではなく、*/それ自体に展開されるため、そのディレクトリに入ることができないというエラーが表示される可能性があります。*/cd*/

では、のように glob 修飾子zshを使用します(またはで終わるのを避けるために を使用します)。Nfor dir in */(N); do...for dir in *(N-/); do...$dir/

ksh93の場合: for dir in ~(N)*/; do....

ではbash、オプションを一時的にグローバルにオンに設定できますnullglob

shopt -s nullglob
for dir in */; do
  ...
done
shopt -u nullglob

$(var)完全を期すために、大きいMakefileコマンドで使用される sの展開make

言語ではawk$はフィールドを参照解除する単項演算子であり、変数varを参照するためにのみ使用しますawk。したがって、はまたは$(var)と同じになり、番目のフィールド (または が 0 の場合はレコード全体)に展開されます。$var$ varvarvar

rcのようなシェルでは、$(var)と同じように$var$ var、も機能します$ 'var'$ ( 'var' )これは、 に渡される 1 つの要素のリストであるため、通常は のみを使用しますが、変数を$逆参照しています。var$var

TCL では、キー$(var)に空の名前を持つ配列変数の値に展開されますvar

$ tclsh
% array set {} {foo 1 bar 2}
% puts $(foo)
1

¹はい、この文脈ではrintPPあまり意味がありません。ご想像のとおり、pwdコマンド(70年代半ばのUnix V5から)は最初で$PWD論理的な現在の作業ディレクトリの処理は、POSIX で規定されているが、後から導入された (80 年代初頭の ksh では、pwd実際にはprint - $PWD(引用符-rと引用符が抜けています!) の別名であり、ドキュメントではresent orking irectory$PWDという略語で呼ばれていた)。 には、同じ目的で( urrent orking irectory) 変数がある。PWDtcsh$cwdcwd

関連情報