![서브쉘을 사용하지 않고 쉘 명령 대체를 수행할 수 있습니까?](https://linux55.com/image/91639/%EC%84%9C%EB%B8%8C%EC%89%98%EC%9D%84%20%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80%20%EC%95%8A%EA%B3%A0%20%EC%89%98%20%EB%AA%85%EB%A0%B9%20%EB%8C%80%EC%B2%B4%EB%A5%BC%20%EC%88%98%ED%96%89%ED%95%A0%20%EC%88%98%20%EC%9E%88%EC%8A%B5%EB%8B%88%EA%B9%8C%3F.png)
서브셸을 사용하지 않고 명령 대체가 필요한 시나리오가 있습니다. 나는 다음과 같은 구조를 가지고 있습니다 :
pushd $(mktemp -d)
이제 임시 디렉토리를 한 번에 종료하고 삭제하고 싶습니다.
rmdir $(popd)
popd
그러나 이는 팝된 디렉터리가 반환되지 않고(새 현재 디렉터리를 반환함) 하위 쉘에서 실행되기 때문에 작동하지 않습니다 .
그것은 마치
dirs -l -1 ; popd &> /dev/null
팝된 디렉토리를 반환하지만 다음과 같이 사용할 수는 없습니다.
rmdir $(dirs -l -1 ; popd &> /dev/null)
popd
서브쉘에만 영향을 미치기 때문입니다 . 필요한 것은 이를 수행하는 능력입니다:
rmdir { dirs -l -1 ; popd &> /dev/null; }
그러나 이는 잘못된 구문입니다. 그러한 효과를 얻을 수 있습니까?
(참고: 임시 디렉터리를 변수에 저장할 수 있다는 것을 알고 있습니다. 그렇게 하지 않고 그 과정에서 새로운 것을 배우려고 노력하고 있습니다!)
답변1
귀하의 질문에 대한 제목 선택이 약간 혼란스럽습니다.
pushd
/는 복사 기능 popd
이며 기억된 디렉토리들을 관리하는 방법입니다.csh
bash
zsh
pushd /some/dir
홍보하다현재의작업 디렉터리를 스택에 추가하고그 다음에현재 작업 디렉터리를 변경한 다음 /some/dir
해당 스택의 내용을 인쇄합니다(공백으로 구분).
popd
스택의 내용을 다시 공백으로 구분하여 인쇄한 다음 스택의 최상위 요소로 변경하고 스택에서 팝합니다.
~/x
(또한 일부 디렉토리는 또는 기호로 표시됩니다 ~user/x
.)
따라서 스택에 현재 디렉터리가 있고 /a
현재 /b
디렉터리가 있고 /here
실행 중인 경우:
pushd /tmp/whatever
popd
pushd
대신 인쇄 /tmp/whatever /here /a /b
하고 popd
출력 합니다 . 이는 명령 대체 사용 여부와 관계가 없습니다. 이전 디렉토리에 대한 경로를 얻는 데 사용할 수 없으며 해당 출력은 일반적으로 사후 처리될 수 없습니다(디렉토리 스택의 요소에 액세스하려면 일부 쉘 또는 배열을 참조하십시오)./here /a /b
/tmp/whatever
popd
$dirstack
$DIRSTACK
아마도 당신은 다음을 원할 것입니다:
pushd "$(mktemp -d)" &&
popd &&
rmdir "$OLDPWD"
또는
cd "$(mktemp -d)" &&
cd - &&
rmdir "$OLDPWD"
그러나 나는 다음을 사용합니다:
tmpdir=$(mktemp -d) || exit
(
cd "$tmpdir" || exit # in a subshell
# do what you have to do in that tmpdir
)
rmdir "$tmpdir"
어떤 경우에도 하위 쉘에서는 실행 pushd "$(mktemp -d)"
되지 않습니다 . pushd
그렇다면 작업 디렉터리를 변경할 수 없습니다. 이는 mktemp
서브 쉘에서 실행됩니다. 별도의 명령이므로 별도의 프로세스에서 실행해야 합니다. 출력을 파이프에 쓰고, 쉘 프로세스는 파이프의 다른 쪽 끝에서 이를 읽습니다.
ksh93은 명령이 내장될 때 별도의 프로세스를 피할 수 있지만 거기에도 여전히 하위 쉘(다른 작업 환경)이 있으며 이번에는 일반적으로 포크에서 제공되는 별도의 환경에 의존하지 않고 에뮬레이트됩니다. 예를 들어 에서는 ksh93
분기 a=0; echo "$(a=1; echo test)"; echo "$a"
가 포함되지 않지만 출력은 여전히 echo "$a"
존재합니다 0
.
mktemp
여기에서 의 출력을 에 전달하는 동안 변수에 저장하려면 다음을 수행하면 됩니다 pushd
.zsh
pushd ${tmpdir::="$(mktemp -d)"}
다른 Bourne과 유사한 쉘의 경우:
unset -v tmpdir
pushd "${tmpdir=$(mktemp -d)}"
또는 출력을 $(mktemp -d)
변수에 명시적으로 저장하지 않고 여러 번 사용하려면 zsh
익명 함수를 사용할 수 있습니다.
(){pushd ${1?} && cd - && rmdir $1} "$(mktemp -d)"
답변2
디렉터리를 떠나기 전에 디렉터리 연결을 해제할 수 있습니다.
rmdir "$(pwd -P)" && popd
또는
rmdir "$(pwd -P)" && cd .. # yes, .. is still usable
그러나 pushd
와 는 popd
실제로 스크립트가 아닌 대화형 셸을 위한 도구입니다(이것이 너무 수다스러운 이유입니다. 실제 스크립트 명령은 성공 시 침묵합니다).
답변3
비슷한 문제가 발생하여 여기에 솔루션을 게시하겠습니다. 분명히 사용할 때 서브쉘 확장을 피할 수 있는 방법이 없으므로 $(command)
제가 얻은 유일한 해결책은 전역 변수를 사용하고 이를 함수에 전달하고 eval
. 제 상황은 좀 까다롭습니다. 함수에서 파일 설명자를 열고 fd 번호를 다시 기본 함수에 전달하고 싶습니다. 문제는 파일 설명자가 기본 셸이 아닌 하위 셸에서 열린다는 것입니다.
open_fd_and_return() {
local _my_file="${1}" _my_fd_no
exec {_my_fd_no}>"${_my_file}" || die "impossible to open the fd"
echo "${_my_fd_no}"
}
my_file="$(mktemp --dry-run)"
my_fd="$(open_fd_and_return "${my_file}")"
echo "test $$" >&"${my_fd}"
rm "${my_file}"
exec {my_fd}>&-
그러면 "잘못된 파일 설명자 오류"가 반환됩니다.
내 현재 솔루션은 eval을 사용하는 것입니다(더 추악하더라도).
open_fd_and_return_eval() {
local _my_var="${1}"
local _my_file="${2}"
local _my_fd_no
exec {_my_fd_no}>"${_my_file}" || die "impossible to open the fd"
eval "${_my_var}=\${_my_fd_no}"
}
my_file="$(mktemp --dry-run)"
my_fd=""
open_fd_and_return_eval "my_fd" "${1}"
echo "test $$" >&"${my_fd}"
rm -- "${my_file}"
exec {my_fd}>&-
이것은 작동합니다. 이것이 누군가에게 유용하기를 바랍니다.
답변4
일반적인 답변
서브쉘을 사용하지 않고 쉘 명령 대체를 수행할 수 있습니까?
문제, 명령 대체가 fish
서브쉘에서 실행되지 않습니다.
$ set dir (cd /etc; and pwd)
$ echo -- $dir $PWD
/etc /etc
현재 작업 디렉토리를 변경하고 dir
.
ksh93 및 최신 버전의 mksh도 ${ cmd; }
하위 쉘에서 실행되지 않는 명령 대체 형식을 지원합니다.
$ dir=${ cd /etc && pwd; }
$ print -r -- "$dir" "$PWD"
/etc /etc
mksh
이를 위해 임시 파일을 사용하십시오.
다른 쉘의 경우 언제든지 임시 파일을 사용할 수 있습니다.
cmd > "$tempfile"
output=$(cat<"$tempfile")