일부 데이터를 동기화하는 간단한 스크립트를 작성하려고 생각했지만 생각보다 어려웠습니다.
기본 레이아웃은 동기화해야 하는 폴더를 참조하는 하위 폴더가 있는 구성 폴더를 갖는 것입니다. 각 폴더에는 [0..2] 파일(includes.txt 및 Excepts.txt)이 포함되어 있습니다. 그런 다음 스크립트는 이를 읽고 동기화 명령을 실행합니다.
내가 실행하고 싶은 것은 다음과 같습니다
me@my_machine:~/scripts$ aws s3 sync /home/me/Pictures s3://my_bucket/home/me/Pictures --exclude "*" --include "*.gif" --include "*.jpg" --profile=personal --dryrun
(dryrun) upload: ../Pictures/sample_picture.jpg to s3://my_bucket/home/me/Pictures/sample_picture.jpg
따라서 특정 파일을 무시할 수 있습니다. AWS CLI에서는 패턴을 큰따옴표로 묶어야 하기 때문에 스크립트에서 제외 및 포함을 가져올 수 없습니다.
내가 읽은 다른 질문에는 배열과 함수 사용에 대한 내용이 나와 있으므로 내 스크립트는 다음과 같습니다.
#!/bin/bash
set -x
DRYRUN=true
s3_bucket_uri='s3://my_bucket'
aws_profile='--profile=personal'
config_folder='../config/*'
include_file='includes.txt'
exclude_file='excludes.txt'
includes=()
excludes=()
sync () {
local params=()
local local_folder="$HOME/$1"
local bucket_folder="$s3_bucket_uri""$local_folder"
params+=("$local_folder" "$bucket_folder")
if [[ ${excludes[@]} ]]; then
params+=("${excludes[@]/#/--exclude }")
fi
if [[ ${includes[@]} ]]; then
params+=("${includes[@]/#/--include }")
fi
params+=("$aws_profile")
if [[ "$DRYRUN" = true ]]; then
params+=(--dryrun)
fi
aws s3 sync ${params[@]}
}
read_parameters () {
if [[ -f "$1" ]]; then
while read line; do
if [[ $2 == "include" ]]; then
includes+=("$line")
elif [[ $2 == "exclude" ]]; then
excludes+=("$line")
fi
done < $1
fi
}
reset () {
includes=()
excludes=()
}
for folder in $config_folder; do
if [[ -d "$folder" && ! -L "$folder" ]]; then
read_parameters $folder/$exclude_file exclude
read_parameters $folder/$include_file include
sync "${folder##*/}"
reset
fi
done
입력 예는 다음과 같습니다.
"*.jpg"
"*.gif"
내부에.txt를 포함합니다문서.
문제는 포함 및 제외 패턴에 큰따옴표가 필요하기 때문에 AWS CLI에 대한 따옴표를 올바르게 가져오는 것입니다. 이는 올바르게 가져오기 어려워 보입니다.
를 사용하면 aws s3 sync ${params[@]}
셸은 패턴 주위에 추가 작은따옴표를 추가합니다. 이로 인해 명령이 충돌하지는 않지만 모든 패턴이 무시됩니다.
+ aws s3 sync /home/me/Pictures s3://mybucket/home/me/Pictures --exclude '"*"' --include '"*.gif"' --include '"*.jpg"' --profile=personal --dryrun
(dryrun) upload: ../../../Pictures/Bender_Rodriguez.png to s3://mybucket/home/me/Pictures/Bender_Rodriguez.png
보시다시피 .gif 및 .jpg 파일을 제외한 모든 파일을 제외하도록 지시하고 있으므로 제외되어야 하는 콘텐츠를 업로드하려고 합니다.
셸은 aws s3 sync "${params[@]}"
전체 include 또는 제외 문 주위에 작은따옴표를 추가하여 명령이 충돌하도록 합니다.
+ aws s3 sync /home/me/Pictures s3://mybucket/home/me/Pictures '--exclude "*"' '--include "*.gif"' '--include "*.jpg"' --profile=personal --dryrun
Unknown options: --exclude "*",--include "*.gif",--include "*.jpg"
params+=(--testing "foobar")
또한 다른 질문에 제공된 접근 방식이므로 수동으로 생성된 값을 추가해 보았습니다 . 그러나 이로 인해 모든 따옴표가 손실되고 최종 결과는 다음과 같습니다.
+ aws s3 sync /home/me/Pictures s3://mybucket/home/me/Pictures --testing foobar --profile=personal --dryrun
확인했어요이 문제, 그러나 그 대답에도 불구하고 나는 다음을 얻습니다.
bar=( --bar a="b" )
cmd=(foo "${bar[@]}" )
printf '%q ' "${cmd[@]}" && echo # print code equivalent to the command we're about to run
"${cmd[@]}" # actually run this code
+ bar=(--bar a="b")
+ cmd=(foo "${bar[@]}")
+ printf '%q ' foo --bar a=b
foo --bar a=b + echo
+ foo --bar a=b
따라서 큰따옴표가 손실됩니다.
다른 경우를 대비해 내 Bash 버전은 다음과 같습니다.
me@my_machine:~/scripts$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
그렇다면 이 문제를 해결할 수 있는 방법이 있습니까? 아니면 프로그래밍 언어로 스크립트를 다시 작성하고 셸 스크립트와 AWS CLI를 사용하는 대신 AWS SDK를 사용해야 합니까?
@muru: 따옴표를 넣지 않으면 포함 및 제외 패턴이 사용되지 않습니다.
me@my_machine:~/scripts$ aws s3 sync /home/me/Pictures s3://my_bucket/home/me/Pictures --exclude * --include *.gif --include *.jpg --profile=personal --dryrun
(dryrun) upload: ../Pictures/Bender_Rodriguez.png to s3://my_bucket/home/me/Pictures/Bender_Rodriguez.png
(dryrun) upload: ../Pictures/Panttaus/sormus_paalta.png to s3://my_bucket/home/me/Pictures/Panttaus/sormus_paalta.png
(dryrun) upload: ../Pictures/Panttaus/sormus_sivusta.png to s3://my_bucket/home/me/Pictures/Panttaus/sormus_sivusta.png
(dryrun) upload: ../Pictures/Screenshot from 2021-03-13 22-30-26.png to s3://my_bucket/home/me/Pictures/Screenshot from 2021-03-13 22-30-26.png
(dryrun) upload: ../Pictures/willow_7_months.jpg to s3://my_bucket/home/me/Pictures/willow_7_months.jpg
큰따옴표가 작은따옴표 안에 있는 경우 set -x
다음을 실행하면 입력이 표시됩니다.
me@my_machine:~/scripts$ aws s3 sync /home/me/Pictures s3://my_bucket/home/me/Pictures --exclude '"*"' --include '"*.gif"' --include '"*.jpg"' --profile=personal --dryrun
(dryrun) upload: ../Pictures/Bender_Rodriguez.png to s3://my_bucket/home/me/Pictures/Bender_Rodriguez.png
(dryrun) upload: ../Pictures/Panttaus/sormus_paalta.png to s3://my_bucket/home/me/Pictures/Panttaus/sormus_paalta.png
(dryrun) upload: ../Pictures/Panttaus/sormus_sivusta.png to s3://my_bucket/home/me/Pictures/Panttaus/sormus_sivusta.png
(dryrun) upload: ../Pictures/Screenshot from 2021-03-13 22-30-26.png to s3://my_bucket/home/me/Pictures/Screenshot from 2021-03-13 22-30-26.png
(dryrun) upload: ../Pictures/willow_7_months.jpg to s3://my_bucket/home/me/Pictures/willow_7_months.jpg
제외 및 포함 패턴은 질문에서 언급한 대로 큰따옴표가 올바르게 유지되는 경우에만 작동합니다.
입력에서 따옴표를 완전히 제거하면 다음과 같습니다.
.jpg
.gif
그리고 스크립트에 아무것도 추가하려고 하지 마세요:
aws s3 sync ${params[@]}
결과는 작은따옴표입니다.
+ aws s3 sync /home/me/Pictures s3://my_bucket/home/me/Pictures --exclude '*' --include '*.gif' --include '*.jpg' --profile=personal --dryrun
(dryrun) upload: ../Pictures/Bender_Rodriguez.png to s3://my_bucket/home/me/Pictures/Bender_Rodriguez.png
(dryrun) upload: ../Pictures/Panttaus/sormus_paalta.png to s3://my_bucket/home/me/Pictures/Panttaus/sormus_paalta.png
(dryrun) upload: ../Pictures/Panttaus/sormus_sivusta.png to s3://my_bucket/home/me/Pictures/Panttaus/sormus_sivusta.png
(dryrun) upload: ../Pictures/Screenshot from 2021-03-13 22-30-26.png to s3://my_bucket/home/me/Pictures/Screenshot from 2021-03-13 22-30-26.png
(dryrun) upload: ../Pictures/willow_7_months.jpg to s3://my_bucket/home/me/Pictures/willow_7_months.jpg
다시 말하지만, .png 파일은 무시되지 않습니다.
그리고 스크립트에 따옴표를 넣으세요.
aws s3 sync "${params[@]}"
전체 인수를 참조합니다.
+ aws s3 sync /home/me/Pictures s3://my_bucket/home/me/Pictures '--exclude *' '--include *.gif' '--include *.jpg' --profile=personal --dryrun
Unknown options: --exclude sync.sh,--include *.png,--include *.jpg
또한 스크립트를 단순화하면 다음과 같습니다.
#!/bin/bash
set -x
DRYRUN=true
s3_bucket_uri='s3://my_bucket'
aws_profile='--profile=personal'
backup_config_folder='../config/*'
include_file='includes.txt'
exclude_file='excludes.txt'
includes=()
excludes=()
sync () {
local params=()
local local_folder="$HOME/$1"
local bucket_folder="$s3_bucket_uri""$local_folder"
params+=("$local_folder" "$bucket_folder")
if [[ ${excludes[@]} ]]; then
params+=("${excludes[@]}")
fi
if [[ ${includes[@]} ]]; then
params+=("${includes[@]}")
fi
params+=("$aws_profile")
if [[ "$DRYRUN" = true ]]; then
params+=(--dryrun)
fi
aws s3 sync "${params[@]}"
}
read_parameters () {
if [[ -f "$1" ]]; then
while read line; do
if [[ $2 == "include" ]]; then
includes+=(--include "$line")
elif [[ $2 == "exclude" ]]; then
excludes+=(--exclude "$line")
fi
done < $1
fi
}
reset () {
includes=()
excludes=()
}
for folder in $backup_config_folder; do
if [[ -d "$folder" && ! -L "$folder" ]]; then
read_parameters $folder/$exclude_file exclude
read_parameters $folder/$include_file include
sync "${folder##*/}"
reset
fi
done
출력에 작은 따옴표를 붙이면 마침내 작동했습니다.
+ aws s3 sync /home/me/Pictures s3://my_bucket/home/me/Pictures --exclude '*' --include '*.gif' --include '*.jpg' --profile=personal --dryrun
(dryrun) upload: ../../../Pictures/willow_7_months.jpg to s3://my_bucket/home/me/Pictures/willow_7_months.jpg
그래서 제 생각에 교훈은: 애초에 큰따옴표를 사용하려고 하지 마세요.
답변1
디버그 출력을 잘못 해석했습니다 set -x
. Bash는 결과로 실행된 명령을 기록할 때 set -x
표준 인용문을 표시하며 , 여기서 인용 제거를 적용하여 사용된 실제 명령을 파생할 수 있습니다.
foo "a b"
공백이 포함된 매개변수와 같은 명령이 있다고 가정해 보겠습니다 . bash가 이를 로 기록해야 할 때 , 그것이 단일 인수임을 set -x
표시하는 방법이 필요합니다 . a b
따라서 출력에 참조된 버전이 표시됩니다. 명령줄에서 해당 버전을 사용하면 다음과 같은 출력이 표시됩니다.
$ foo a\ b
+ foo 'a b'
foo
인수를 사용하여 명령을 실행해야 한다고 가정하면 a"b"
, 즉 명령이 이러한 따옴표를 받아야 한다고 가정하면, bash에서는 일반적으로 다음 중 하나의 변형을 실행합니다.
foo 'a"b"'
foo a\"b\"
foo 'a"b'\"
이제 이 명령을 기록해야 할 때 bash는 이제 따옴표도 인용되어 있음을 표시해야 합니다. 그렇지 않으면 사람들은 이러한 인용문이 첫 번째 코드 블록의 문제로 인한 것이라고 생각할 수 있습니다. 그래서 우리는 다음을 얻습니다:
$ foo a\"b\"
+ foo 'a"b"'
Bash는 아무것도 추가하거나 제거하지 않습니다. 단지 실행될 내용을 따옴표로 명확하게 표시할 뿐입니다.
'"*"'
따라서 디버그 출력에서 이를 보면 bash가 작은따옴표를 추가하는 것을 볼 수 없습니다. Bash는 따옴표를 제거한 후에 이를 가져오는 것을 보여주려고 하므로 "*"
큰따옴표를 명령에 전달해야 합니다. 큰따옴표가 어디서 나오는지 궁금하실 겁니다. 아마도 입력 파일에서 나온 것 같습니다.
이 두 코드 블록은 너무 복잡합니다.
if [[ ${excludes[@]} ]]; then
params+=("${excludes[@]/#/--exclude }")
fi
if [[ ${includes[@]} ]]; then
params+=("${includes[@]/#/--include }")
fi
if [[ $2 == "include" ]]; then
includes+=($line)
elif [[ $2 == "exclude" ]]; then
excludes+=($line)
fi
읽기 모드에서 옵션을 추가하세요:
if [[ $2 = include ]]; then
includes+=(--include "$line")
elif [[ $2 = exclude ]]; then
excludes+=(--exclude "$line")
fi
그런 다음 추가 조작 없이 이러한 배열을 직접 사용할 수 있습니다.
물론, 필드 분할, 파일 이름 확장 등을 수행하려는 경우가 아니면 변수 주위에 따옴표를 사용하는 것을 잊지 마십시오.