SSH 찾기 명령이 로컬 변수에 저장된 디렉터리를 인식하지 못함

SSH 찾기 명령이 로컬 변수에 저장된 디렉터리를 인식하지 못함

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에 대한 인수(전자는 큰따옴표로 묶이고 후자는 작은따옴표로 묶음)입니다.

-cSsh는 모든 "명령" 인수를 공백으로 결합한 후 원격 시스템의 사용자 로그인 셸 옵션 에 단일 인수로 전달합니다 .

원격 명령 자체에 작은따옴표가 포함되어야 하는 경우 명령 대체 + 여기 문서 콤보를 사용할 수 있습니다. 또한 "로컬" 변수의 값에 공백 및 기타 특수 문자가 포함될 수 있는 경우 이를 이스케이프해야 합니다.

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을 통해 원격 스크립트를 전달하는 것은 거의 필요하지 않습니다.

관련 정보