![SSH 찾기 명령이 로컬 변수에 저장된 디렉터리를 인식하지 못함](https://rvso.com/image/170291/SSH%20%EC%B0%BE%EA%B8%B0%20%EB%AA%85%EB%A0%B9%EC%9D%B4%20%EB%A1%9C%EC%BB%AC%20%EB%B3%80%EC%88%98%EC%97%90%20%EC%A0%80%EC%9E%A5%EB%90%9C%20%EB%94%94%EB%A0%89%ED%84%B0%EB%A6%AC%EB%A5%BC%20%EC%9D%B8%EC%8B%9D%ED%95%98%EC%A7%80%20%EB%AA%BB%ED%95%A8.png)
SSH를 통해 로컬 변수에 저장된 디렉토리로 이동할 수 있습니다 $out_dir
. 그러나 find | head
명령이 작동하지 않는 이유를 알 수 없습니다 . ~ 안에#2를 시도해 보세요, 명령 끝에 두 번째 백슬래시를 추가했습니다 find
.
#1 오류를 시도해보세요: find: `/dir/on/remote/server': No such file or directory
ssh $user@$ip << EOF
discard=$( find $out_dir -exec basename {} \; | head -n -1 )
for f in $discard; do
echo "rm $f"
done
logout
EOF
#2 오류를 시도해보세요: syntax error near unexpected token `|'
ssh $user@$ip << EOF
discard=$( find $out_dir -exec basename {} \\; | head -n -1 )
for f in $discard; do
echo "rm $f"
done
logout
EOF
답변1
시도 #1에서 '해당 파일 또는 디렉토리가 없습니다'라는 메시지가 표시되는 이유는 heredoc( <<EOF
)에 대한 모든 변수 및 명령 보간이 원격 호스트로 전송되기 전에 로컬에서 수행되기 때문입니다.
올바르게 언급했듯이 $out_dir
보간되고 있습니다. 보고 싶은 디렉토리가 보입니다. 이는 SSH 호출을 하기 전에 로컬 컴퓨터에서 발생합니다. 명령 대체( $()
)는 로컬에서도 발생합니다(그러나 원격으로 수행하려고 합니다). 다르게 말하면, 찾기 전체가 원격 시스템으로 전송되기 전에 로컬에서 처리됩니다. 래핑된 모든 항목은 $()
heredoc에 의해 처리됩니다. 따라서 ${out_dir}
로컬 컴퓨터에 존재하지 않는 것 같습니다. '해당 파일이나 디렉터리가 없습니다'.
이것을 더 잘 보려면 예제를 간략히 살펴보겠습니다. 이 시도:
$ ssh foo@localhost <<EOF
echo "using account: $(whoami)"
EOF
Pseudo-terminal will not be allocated because stdin is not a terminal.
<truncated>
using account: vagrant
내 로컬 계정이 '방랑'하므로 $()의 내용이 로컬에서 실행되고 있음이 분명합니다. 올바르게 작동한다면 내가 지정했기 때문에 계정은 'foo'가 되었을 것입니다 ssh foo@localhost
.
시도 #2는 ';' 대신 '\'를 이스케이프했기 때문에 깨졌습니다. 찾기에는 종료가 필요 -exec
하지만 지금은 그렇지 않습니다. 대신 명령이 갑자기 종료되고 ';'이 매달려 있습니다. bash 명령이 종료됩니다. '|' 일부 입력을 전달할 것으로 예상하지만 지정된 것이 없습니다. 실제로 다음을 만들었습니다.
$ | cat
-bash: syntax error near unexpected token `|'
그렇다면 효과적인 해결책은 무엇입니까?
글쎄, find
당신을 위해 삭제를 수행할 수 있습니다:
ssh $user@$ip <<EOF
find $out_dir -delete
EOF
간단하고 직접적입니다. 일부 파일을 선택하고 다른 파일을 제외하는 것이 걱정된다면 find
.
find
현재 버전에서는 일부 극단적인 경우 처리를 시도하는 것처럼 보이기 때문에 '수정' 조언을 제공합니다 .
- 기본 이름 사용
- 사용
head -n -1
하지만 이 처리가 의도한 대로 작동할 것으로 기대하지는 않습니다.
첫째, 호출하면 basename
대부분의 경로가 제거되며 ssh 명령에서는 디렉토리를 변경하기 위해 아무 것도 수행하지 않습니다. 귀하의 SSH 실행은 ${user}의 집과 관련된 모든 것을 삭제하려고 시도할 것이며 ${out_dir}을 지정할 때 집이 아닌 다른 디렉토리를 사용하려는 것 같습니다! 다시 말하지만, find
삭제 작업을 수행해 보세요.
둘째, 나는 이것에 대해 기본이 아닐 수도 있지만 head -n -1
대상 파일 목록에서 ${out_dir}을 제거하는 데 사용하는 것 같습니다.
$ find junk/
junk/
junk/one
junk/two
junk/three
맞습니다. 삭제하고 싶지 않습니다 junk/
.
그러나 다음을 시도해 보십시오.
$ seq 1 4
1
2
3
4
$ seq 1 4 | head -n -1
1
2
3
이는 첫 번째 줄을 유지하지 않고 마지막 줄을 유지합니다. 꼬리를 시험해 보십시오:
$ seq 1 4 | tail -n +2
2
3
4
하지만 다시 한 번 말씀드리지만, find
삭제 작업을 직접 수행해 보세요.
파일만 제거하기 위해 find가 필요한 경우 를 살펴보십시오 -type f
. 그러나 고급 필터링을 위한 제외 및 포함 패턴도 있습니다.
답변2
이 시도:
ssh "$user@$ip" "out_dir=$out_dir;" '
discard=$( find $out_dir -exec basename {} \; | head -n -1 )
for f in $discard; do
echo "rm $f"
done
'
메모:의도한 원격 명령을 더 간단하고 안전하게 만들 수 있지만 그렇게 하면 요점이 혼란스러울 뿐입니다.
기본적으로 다음과 같은 부분을 통과해야 합니다.ㅏ)~해야 한다또한에 확장되다현지의기계와 그 것비)해야한다오직에 확장되다원격기계로분리된ssh에 대한 인수(전자는 큰따옴표로 묶이고 후자는 작은따옴표로 묶음)입니다.
-c
Ssh는 모든 "명령" 인수를 공백으로 결합한 후 원격 시스템의 사용자 로그인 셸 옵션 에 단일 인수로 전달합니다 .
원격 명령 자체에 작은따옴표가 포함되어야 하는 경우 명령 대체 + 여기 문서 콤보를 사용할 수 있습니다. 또한 "로컬" 변수의 값에 공백 및 기타 특수 문자가 포함될 수 있는 경우 이를 이스케이프해야 합니다.
out_dir='/path/to/ * happiness * '
out_dir=$(printf %s "$out_dir" | sed 's/[^a-zA-Z0-9_/.]/\\&/g')
ssh localhost out_dir="$out_dir;" "$(cat <<'EOT'
echo '<$out_dir>' = "<$out_dir>"
EOT
)"
<<'EOF'
; 의 작은따옴표에 주목하세요 . 생략하면 $out_dir
여기 문서의 이 확장됩니다(로컬 컴퓨터에서).
최신 버전의 bash에는 형식이 있으므로 그 추악한 대신 ${var@Q}
간단히 사용할 수 있습니다 ."out_dir=${outdir@Q};"
echo | sed
stdin을 통해 원격 스크립트를 전달하는 것은 거의 필요하지 않습니다.