쉘 스크립트를 작성하고 있습니다. (쉘을 사용하는 버전과 운영체제는 아래와 같습니다.) 이 스크립트는 로컬 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/
할 수 있도록 하고 싶은데 위의 내용이 출력되고 있습니다 . 스크립트는 로 시작합니다 . 시도하는 동안 오류가 발생합니다. 다른 문제를 발견했습니다cd
cw-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_SUBDIRECTORY
MY_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 | tee
where 번들 의 tee
표준 출력에 대한 일반 메시지 및 오류 메시지에 대한 또 다른 개선 사항 입니다 cd
.
또 다른 개선 사항은 종료 상태가 보존된다는 것입니다 cd
. 실패할 경우 스크립트의 나머지 부분을 계속 진행하지 않도록 || exit
하기 위해 하나를 추가했습니다 .cd
와 같은 것들은 디렉토리 스택을 탐색할 때 stdout에서만 출력됩니다 cd
. 여기서는 그렇지 않으므로 그냥 사용할 수 있습니다.cd -
cd -2
cd +2
cd ... 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 +x
xtrace
() {
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"
또한 이것은 pwd
stuff 를 인쇄하는 내장 명령 $PWD
이므로 $(pwd)
약간 의미가 없습니다.
print -r "PWD Working Directory is $PWD"
더 이해가 될 것입니다.