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에서 "해당 파일 또는 디렉터리가 없습니다"라는 메시지가 표시되는 이유는 <<EOF
heredoc(the )의 모든 변수 및 명령 보간이 원격 호스트로 전송되기 전에 로컬에서 수행되기 때문입니다.
올바르게 알아차렸듯이 $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
내 로컬 계정이 "vagrant"이기 때문에 분명히 $()의 내용은 로컬에서 실행됩니다. 올바르게 작동했다면 계정은 "foo"가 될 것입니다 ssh foo@localhost
.
";" 대신 "\"를 이스케이프했기 때문에 시도 #2가 중단되었습니다. Find를 -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
이를 제거하도록 도와드리겠습니다.
파일 삭제만 필요한 경우 살펴보세요 -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
표준 입력을 통해 원격 스크립트를 전달하는 것은 거의 필요하지 않습니다.