다음과 같은 일련의 명령이 있습니다.
ssh -i key 10.10.10.10 mkdir -p "${DEST_PATH}/subdir1" "${DEST_PATH}/subdir2"
rsync "${SOURCE_PATH}" "$DEST_HOST:${DEST_PATH}/subdir1"
...
이러한 공급 변수는
DEST_PATH=$( myfun "foo" )
SOURCE_PATH=$( myfun "bar" )
이는 차례로 문자열을 공급합니다.
function myfun {
local VALUE=$( grep "$1" yadayada.txt | tail -1 | sed 's/someregex//' )
echo "$VALUE"
}
$VALUE
공간이 있다면 $VALUE
무엇을 하시겠습니까 Hello World
? 나는 그것을 항상 사용하면 ""
모든 문제가 해결될 것이라고 생각했지만, 내 생각은 틀렸습니다. 다른 몇 가지 질문에서는 작은 따옴표를 피하거나 사용하는 것을 제안하지만 가장 좋은 접근 방식이 무엇인지 잘 모르겠습니다.
감사해요
답변1
xargs
원격 시스템에 특정 옵션을 지원하는 명령이 있다는 것을 알고 있는 경우 -0
다음을 수행할 수 있습니다.
printf '%s\0' "$DEST_PATH/subdir1" "$DEST_PATH/subdir2" |
ssh -i key 10.10.10.10 'xargs -0 mkdir -p --'
모든 쉘은 xargs -0 mkdir -p --
이 쉘 코드를 동일한 방식으로 해석합니다( sshd
원격 사용자의 로그인 쉘은 주어진 코드를 해석하기 위해 원격 시스템에서 실행 중이라는 점을 기억하십시오 ssh
).
mkdir
원격 셸의 stdin을 통해 전달된 인수 목록 은 NUL로 구분되며 xargs
.
이 방법의 또 다른 장점은 원하는 만큼의 매개변수를 전달할 수 있다는 것입니다. 필요한 경우 xargs
목록이 손상되고 mkdir -p -- dirn dirn+1...
매개변수+환경 목록 크기 제한을 우회하기 위해 여러 호출이 실행되며, 매우 큰 목록의 전체 목록이 전송되기 전에 디렉터리 생성을 시작할 수도 있습니다.
바라보다원격 사용자의 로그인 셸을 모르고 SSH를 통해 임의의 간단한 명령을 어떻게 실행할 수 있습니까?다른 방법의 경우.
rsync
기본적으로 동기화를 위해 원격 셸 명령줄의 일부로 원격 파일/디렉터리 이름을 전달하므로 자체적으로 동일한 문제가 있습니다. 더 나은 해결책은 셸 코드에서 파일/디렉터리 이름을 전달하는 대신 -s
/를 사용하는 것입니다. --protect-args
, 대신 rsync
서버의 표준 입력으로 전달하세요.
그래서:
rsync -s -- "$SOURCE_PATH" "$DEST_HOST:$DEST_PATH/subdir1/"
매뉴얼 rsync
페이지를 인용하면 다음과 같습니다.
공백이 포함된 파일 이름을 전송해야 하거나 원격 쉘이 이해하는 방식으로 공백을 이스케이프해야 하는 경우
--protect-args
( ) 옵션을 지정할 수 있습니다.-s
예를 들어:rsync -av host:'file\ name\ with\ spaces' /dest
$ rsync --rsync-path='set -x; rsync' /etc/issue localhost:'1 2'
+zsh:1> rsync --server -e.LsfxC . 1 2
$ rsync --rsync-path='set -x; rsync' /etc/issue localhost:'1;uname>&2'
+zsh:1> rsync --server -e.LsfxC . 1
+zsh:1> uname
Linux
파일 이름이 쉘 코드로 어떻게 해석되는지 확인하세요.
그리고 -s
:
~$ rsync -s --rsync-path='set -x; rsync' /etc/issue localhost:'1;uname>&2'
+zsh:1> rsync --server -se.LsfxC
원격 쉘 명령줄에는 파일 이름에 대한 추적이 없습니다.
~$ cat '1;uname>&2'
Ubuntu 20.04.1 LTS \n \l
답변2
큰따옴표를 사용하여 해결가장 자주 묻는 질문, 그러나 이것이 모든 문제를 해결하는 것은 아닙니다. 귀하의 경우 큰따옴표는 로컬 셸에서 불필요한 확장을 방지하지만 명령은 ssh
보호 없이 파일 이름을 셸 조각에 직접 삽입하는 반면 rsync는 명령줄에서 원격 파일 이름을 전달합니다. 하단 레이어는 동일한 작업을 수행합니다. 즉, 그렇다면 DEST_PATH
명령 /path/with three spaces
을 실행하고 있는 것입니다.
mkdir -p /path/with three spaces/subdir1 /path/with three spaces/subdir2
원격 컴퓨터에서. 큰따옴표는 $DEST_PATH
스크립트를 실행하는 셸이 값을 과도하게 분할하지 않도록 하지만 원격 셸에서는 공백이 의미가 있음을 보장합니다.
중첩된 따옴표를 순진하게 사용하면아니요이 문제를 해결하세요. 예를 들어, ssh -i key 10.10.10.10 mkdir -p "'${DEST_PATH}/subdir1'" "'${DEST_PATH}/subdir2'"
공백이 포함된 파일 이름을 사용할 수 있지만 공백이 포함된 파일 이름은 '
작동하지 않습니다(공격자가 의 값을 제어하는 경우 이는 원격 명령 실행 취약점일 수 있음 DEST_PATH
).
가장 쉬운 방법은 달리는 것입니다sshfs모든 것을 로컬 경로로 처리합니다. ssh
또는 명령에서 파일 이름을 전달할 필요가 없다면 rsync
파일 이름을 인용하는 것에 대해 걱정할 필요가 없습니다. 그러나 sshfs는 항상 관리하기 쉬운 것은 아니며 rsync를 통해 대용량 파일을 효율적으로 업데이트할 수 없습니다.
간단한 접근 방식이 효과가 없다면 인용된 값이 필요합니다 DEST_PATH
. 즉, 또는 와 같은 것으로 변경해야 합니다 /path/with three spaces
.'/path/with three spaces'
/path/with\ three\ \ spaces
set_quoted () {
raw="$1"
quoted=
while case "$raw" in *\'*) true;; *) false;; esac; do
quoted="$quoted'\\''${raw%%\'*}"
raw="${raw#*\'}"
done
quoted="'$quoted$raw'"
}
set_quoted "$DEST_PATH"; quoted_DEST_PATH="$quoted"
ssh -i key 10.10.10.10 mkdir -p "${quoted_DEST_PATH}/subdir1" "${quoted_DEST_PATH}/subdir2"
rsync "${SOURCE_PATH}" "$DEST_HOST:${quoted_DEST_PATH}/subdir1"
1 Rsync는 재귀 또는 로컬 파일의 임의 파일 이름에 문제가 없습니다. 그러나 원격 쉘에 명령줄 인수를 원격 파일 이름으로 전달하고 원격 쉘은 이를 확장합니다.
답변3
이는 인수를 쉘 입력으로 재사용할 수 있는 GNU 시스템이 있는 경우 printf
지원됩니다 .%q
그래서 당신은 할 수 있습니다
DEST_PATH_ESC="$(printf "%q" "$DEST_PATH")
ssh -i key 10.10.10.10 mkdir -p "${DEST_PATH_ESC}/subdir1" "${DEST_PATH_ESC}/subdir2"
rsync "${SOURCE_PATH}" "$DEST_HOST:${DEST_PATH_ESC}/subdir1" # not sure if you need it here
테스트해보자:
a="some complicated thing with spaces
and such"
b=$(printf "%q" "$a")
echo "$b"
우리는 얻었다
$'some complicated thing with spaces\nand such'
따라서 이것은 공백뿐만 아니라 줄 바꿈에 대해서도 매우 안전해야 합니다.