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/directoryA. pwd이 시점의 출력은 예상한 대로입니다( /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한 번에 한 행씩 일치하며 개행 문자로 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 | teewhere 번들 의 tee표준 출력에 대한 일반 메시지 및 오류 메시지에 대한 또 다른 개선 사항 입니다 cd.

또 다른 개선 사항은 종료 상태가 보존된다는 것입니다 cd. 실패할 경우 스크립트의 나머지 부분을 계속 진행하지 않도록 || exit하기 위해 하나를 추가했습니다 .cd

와 같은 것들은 디렉토리 스택을 탐색할 때 stdout에서만 출력됩니다 cd. 여기서는 그렇지 않으므로 그냥 사용할 수 있습니다.cd -cd -2cd +2cd ... 2>&2 2>> file.log

또한 이 방법이나 사용자의 방법 모두 출력을 리디렉션하지 tee않습니다 xtrace. stderr로 이동하지만 자체적으로 출력하지는 않습니다 . xtrace 출력을 캡처하려면 그 안에서 실행 중인 명령 그룹 cd을 리디렉션해야 합니다 . cd그것은 다음과 같습니다:

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

함수나 익명 함수에서 로컬로 옵션을 활성화할 수도 있으며 를 실행하는 대신 set -x추적 set +x이 표시됩니다.set +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 -t을 사용하여 ksh에서 함수 추적을 활성화합니다.

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"

더 이해가 될 것입니다.

관련 정보