zshシェルで実行されるシェルスクリプトでディレクトリを変更する方法

zshシェルで実行されるシェルスクリプトでディレクトリを変更する方法

シェルスクリプトを作成しています。 (シェルを使用するバージョンとオペレーティングシステムは次のとおりです。)このスクリプトは、ローカルのgitリポジトリ/ディレクトリで複数のタグを含むコマンドを実行します。スクリプトは、リポジトリタグ名に基づいて選択されたディレクトリのgrepファイルからいくつかの文字列を出力します。

zsh --version
zsh 5.9 (x86_64-apple-darwin22.0)
OS: System Version: macOS 13.6.3
+
using oh-my-zsh, https://ohmyz.sh/

このスクリプトはcdディレクトリ(コマンド)をgitローカルリポジトリに変更します。次に、配列のラベルを取得するプロセスを経ますMY_ARRAY=($(git tag))。次に、各ストレージタグに対してgit checkout $my_tag --quietそのタグに対してアクションを実行します。その後、cdリポジトリのディレクトリ(directoryA)にあります。次に、pwd作業ディレクトリを確認するためにaを実行します/git-repository/directoryApwdこの時点の出力は予想通りです(/git-repository/directoryA)。これがあると、/git-repository/directoryAスクリプトはLIST_OF_SUBDIRECTORIES=($(ls -d */))のサブディレクトリ配列を取得します。directoryA出力は次のとおりですLIST_OF_SUBDIRECTORIES is cw-events/ cw-metric-alarm/ eventbridge-scheduler/ msk/ secrets-manager/ ses/ sns-topic/ sqs/ ssm-params/ xlate/。各サブディレクトリ(forループを使用)に対して、スクリプトは次のcdようにLIST_OF_SUBDIRECTORIES配列のサブディレクトリを指す必要があります。

if echo "$my_tag" | grep -q -E "$MY_SUBDIRECTORY"; then
          echo "MY_SUBDIRECTORY is $MY_SUBDIRECTORY"
          set -x
          cd $MY_SUBDIRECTORY 2>&1 | tee -a log_file.txt
          set +x
          echo "PWD Working Directory is $(pwd)"

if条件の出力がecho "MY_SUBDIRECTORY is $MY_SUBDIRECTORY"trueなのでスクリプトを入力MY_SUBDIRECTORY is cw-events/できるようにしたいのですが上記の内容が出力されています。スクリプトはで始まります。試行中にエラーが発生します。他の問題を発見しましたcdcw-events/echo "PWD Working Directory is $(pwd)"/git-repository/directoryA
#!/bin/zsh#!/bin/bash
https://blog.kubesimplify.com/how-to-change-directory-in-shell-scriptsまたは現在のディレクトリを変更するスクリプト(cd、pwd)しかし実際には、親シェルではなくサブシェルのディレクトリを変更したいと思います。の
中にスクリプト/git-repository/directoryAがにないのはなぜですか?出力例:私ができる簡単な例はスクリプト実行の出力です。cd/git-repository/directoryA/subdirectory-of-A

FIRST-SUB-Directory is /git-repository/directoryA
LIST_OF_SUBDIRECTORIES is cw-events/ secrets-manager/ sqs/ ssm-params/
MY_SUBDIRECTORY is cw-events/
+/path/to/script/script.sh:44> cd cw-events/
+/path/to/script/script.sh:44> tee -a log_file.txt
+/path/to/script/script.sh:45> set +x
PWD Working Directory is /git-repository/directoryA


ありがとうございます。

答え1

この行は明らかに間違っています。

cd $MY_SUBDIRECTORY 2>&1 | tee -a log_file.txt

パイプの左側はサブシェルで実行されます。これは、内部変更(変数、現在のディレクトリなど)がプライマリシェルに影響を与えないことを意味します。バラよりCDをレコーダーにリダイレクトできないロギングの追加の説明とアドバイス。cd -// cd -2...cd +2ディレクトリスタックを検索するときを除いてcd失敗しない限り、出力はエクスポートされないため、出力を記録することはとにかく意味がありません。

cd $MY_SUBDIRECTORY行を(またはより良い場合)に変更すると、cd -- $MY_SUBDIRECTORY少なくとも公開したスクリプト部分に対してディレクトリ変更が機能します。スクリプトの後半でまだ元のディレクトリにある場合は、サブシェルを作成するために別の作業を行っている可能性があります(まだ公開していないコードの一部)。


それ以外には目立って間違ったことはありませんが、いくつかは必要以上に複雑です。間違いのリスクを減らすために、より簡単な方法でタスクを実行することをお勧めします。

  • あなたは使うecho そして set -x そして | tee …文書化する。一つで十分です。set -xほとんどの場合は大丈夫です。パイピングはteeスクリプトの動作(サブシェルの作成、ファイル記述子の変更、エラーの非表示)に影響を与えるため、難しいです。

  • echo "$my_tag" | grep -q -E "$MY_SUBDIRECTORY"値に拡張正規表現演算子(commonタグやgitタグなど)が含まれていないとし、値に部分文字列値がmy_tag含まれていることを確認してください。簡単に確認する方法があります。MY_SUBDIRECTORYMY_SUBDIRECTORY.+

    if [[ $my_tag = *"$MY_SUBDIRECTORY"* ]]; then …
    
  • LIST_OF_SUBDIRECTORIES=($(ls -d */))LIST_OF_SUBDIRECTORIES=( *(-/) )zsh を使用して作成できます。/ グローバル予選、ファイルタイプ、およびその他の特性でグローブをフィルタリングできます。ここで、-so型チェックと組み合わせた操作は、シンボリックリンクの確認後に行われます*/。各要素の末尾にスラッシュは追加されません。…=( *(-/M) )スラッシュが本当に必要な場合や使用可能なディレクトリのみをリストする特別な場合には機能します…=( */ )が、実際にはスラッシュは必要ないかもしれません。Nサブディレクトリがない場合に致命的なエラーの代わりに空のリストを取得するには、glob修飾子を追加してください。

    LIST_OF_SUBDIRECTORIES=( *(N-/) )
    

echo "$my_tag"厳密に言えば、as出力のすべての行はgrep一度に1行ずつ一致し、改行文字にも拡張echoされます\n(幸いにも、gitタグにバックスラッシュが含まれていないことを保証する必要があります)。

答え2

cmd1 | cmd2、同時にcmd1実行されるため、別々cmd2のプロセスで実行する必要があります。

多くのシェルでは、組み込まれていても(たとえばcd)サブプロセスで実行されます。 kshと同様に、zshでもcmd2組み込まれている場合は、現在のシェルプロセスで実行されますが、cmd1常に子プロセスで実行されます。

そのため、cd ... | tee ...サブcdプロセスを実行し、その短期サブプロセスの作業ディレクトリのみを変更してください。

ただし、zshにはtee機能が組み込まれているため、これを行う必要はありません。あなたはできます:

cd -- $MY_SUBDIRECTORY >&1 >> file.log 2>&2 2>> file.log || exit

fdが書き込み用に複数回リダイレクトされると、zshはパイプを介してバックグラウンドteeで同様の機能を実行するプロセスにリダイレクトします。

ここでは fd 1 を>&1( ) にリダイレクトしています。これは通常は機能しませんが、ここでは標準出力をそこに置いて追加モードにリダイレクトするという意味ですfile.log。 fd 2(stderr)でも同じことを行います。これはcd 2>&1 | tee、whereバンドルのtee標準出力に対する一般的なメッセージとエラーメッセージに対するもう1つの改善ですcd

もう1つの改善は、終了状態が保存されることですcd。失敗した場合は、スクリプトの残りの部分を続行しないように|| exitするために1つを追加しました。cd

などのものは、ディレクトリスタックをナビゲートしたときにstdoutからのみ出力されますcd。ここではそうではありませんので、ただ使用できます。cd -cd -2cd +2cd ... 2>&2 2>> file.log

また、この方法もユーザーの方法も出力をリダイレクトしませteextrace。 stderrに移動しますが、それ自体は出力しません。 xtrace出力をキャプチャするには、その中で実行されているコマンドグループcdをリダイレクトする必要があります。cdそれは次のとおりです。

set -x # or set -o xtrace
{
  cd -- $MY_SUBDIRECTORY
} 2>&1 2>> file.log 
set +x

関数または匿名関数でローカルでオプションを有効にすることもでき、実行する代わりにトレースが表示されset -xますset +xset +xxtrace

() {
  set -o localoptions -o xtrace
  cd -- $MY_SUBDIRECTORY
} 2>&2 2>> file.log

または、ヘルパー機能を使用してください。

trace() {
  set -o localoptions -o xtrace
  "$@"
} >&1 >> file.log 2>&2 2>> file.log

trace cd -- $MY_SUBDIRECTORY || exit

または、次のようにfunctions -tkshで関数トレースを有効にします。

trace() {
  "$@"
} >&1 >> file.log 2>&2 2>> file.log
functions -t trace

trace cd -- $MY_SUBDIRECTORY || exit

、、...echoなどのエスケープシーケンスをデフォルトで拡張し、オプションを許可します。改行文字の後にそのまま出力するには、次の手順を実行する必要があります。\n\uXXXX\0123

echo -E - $something

printfただし、通常は(shと互換性があります)または(kshと互換性があり、より一般的な)を使用することをお勧めします。print

printf '%s\n' "$something"
print -r -- "$something"

また、これはpwdstuffを印刷する組み込みコマンド$PWDなので、$(pwd)少し意味がありません。

print -r "PWD Working Directory is $PWD"

もっと理解できるでしょう。

関連情報