В моей системе есть два возможных источника pwd
: pwd
bash
builtin и / usr / bin / pwd
, который принадлежит GNU coreutils. Когда я выдаю cd //; pwd
(используя встроенную оболочку), он набирает //
. Однако, когда я выдаю cd //; / usr / bin / pwd
(используя внешний), он набирает /
. Фактический рабочий каталог в обоих случаях, о чем свидетельствует вывод ls
и т. Д., Всегда один и тот же (корень FS).
Таким образом, ответ, похоже, состоит в том, что здесь задействованы две разные концепции: представление оболочки о своем рабочем каталоге и фактический процесс CWD. Встроенный pwd
запрашивает первый, тогда как / usr / bin / pwd
(и любой другой двоичный файл) будет иметь доступ только ко второму, и только первый знает что-либо о //
.Я понятия не имею, почему концепция рабочего каталога в оболочке различает /
и //
и что должно означать //
.
РЕДАКТИРОВАТЬ: Некоторые поисковые запросы наталкиваются на этот вопрос , который проливает некоторый свет. Судя по всему, странное поведение соответствует стандартам, но все же странно.
В общем, плохая идея понизить список отдельных элементов до одной строки, независимо от того, является ли это списком параметров командной строки или списком путей.
Вместо этого используется массив:
rsync_options=( -rnv --exclude='.*' )
или
rsync_options=( -r -n -v --exclude='.*' )
и позже...
rsync "${rsync_options[@]}" source/ target
Таким образом, цитирование отдельных опций сохраняется (до тех пор, пока вы двойные кавычки расширяете${rsync_options[@]}
). Это также позволяет вам легко манипулировать отдельными элементами массива, если вам это нужно, перед вызовом rsync
.
В любой оболочке POSIX для этого можно использовать список позиционных параметров:
set -- -rnv --exclude='.*'
rsync "$@" source/ target
Опять же, двойное цитирование расширения $@
здесь имеет решающее значение.
Тангенциально связанные:
Проблема в том, что когда вы помещаете два набора параметров в строку, одинарные кавычки значения параметра --exclude
становятся частью этого значения. Следовательно,
RSYNC_OPTIONS='-rnv --exclude=.*'
сработало бы¹... но лучше (, как и в более безопасном ), использовать массив или позиционные параметры с отдельными кавычками. Это также позволит вам использовать вещи с пробелами в них, если вам это нужно, и позволит избежать того, чтобы оболочка выполняла генерацию имени файла (подстановку )для параметров.
¹ при условии, что $IFS
не изменен и в текущем каталоге нет файла, имя которого начинается с --exclude=.
, и не установлены параметры оболочки nullglob
или failglob
.
@Kusalananda уже объяснил основную проблему и способы ее решения, а запись часто задаваемых вопросов Bash , на которую ссылается @glenn jackmann, также содержит много полезной информации. Вот подробное объяснение того, что происходит в моей проблеме, основанное на этих ресурсах.
Мы будем использовать небольшой скрипт, который печатает каждый из своих аргументов в отдельной строке, чтобы проиллюстрировать ситуацию(argtest.bash
):
#!/bin/bash
for var in "$@"
do
echo "$var"
done
Варианты прохождения "вручную":
$./argtest.bash -rnv --exclude='.*'
-rnv
--exclude=.*
Как и ожидалось, части -rnv
и --exclude='.*'
разделены на два аргумента, так как они разделены пробелами без кавычек (это называется разбиением слов).
Также обратите внимание, что кавычки вокруг .*
были удалены :одинарные кавычки говорят оболочке передать их содержимое без специальной интерпретации , , но сами кавычки не передаются в команда .
Если теперь мы будем хранить параметры в переменной в виде строки (вместо использования массива ), то кавычки не удалятся:
$ OPTS="--exclude='.*'"
$./argtest.bash $OPTS
--exclude='.*'
Это происходит по двум причинам: :двойные кавычки, используемые при определении $OPTS
, не позволяют специальной обработке одинарных кавычек, поэтому последние являются частью значения:
$ echo $OPTS
--exclude='.*'
Теперь, когда мы используем $OPTS
в качестве аргумента команды, кавычки обрабатываются до раскрытия параметра , поэтому кавычки в $OPTS
появляются "слишком поздно".
Это означает, что (в моей исходной задаче)rsync
использует шаблон исключения'.*'
(с кавычками!)вместо шаблона.*
--он исключает файлы, имя которых начинается с одинарной кавычки, за которой следует точка, и заканчивается одинарной кавычкой. Очевидно, это не то, что было задумано.
В качестве обходного пути можно было бы опустить двойные кавычки при определении$OPTS
:
$ OPTS2=--exclude='.*'
$./argtest.bash $OPTS2
--exclude=.*
Тем не менее, рекомендуется всегда заключать присвоения переменных в кавычки из-за тонких различий в более сложных случаях.
Как заметил @Kusalananda, не цитирование .*
также сработало бы. Я добавил кавычки, чтобы предотвратить расширение шаблона , но это не было строго необходимо в этом особом случае:
$./argtest.bash --exclude=.*
--exclude=.*
Оказывается, Bash действительно выполняет раскрытие шаблона, но шаблон --exclude=.*
не соответствует ни одному файлу, поэтому шаблон передается команде. Сравните:
$ touch some_file
$./argtest.bash some_*
some_file
$./argtest.bash does_not_exit_*
does_not_exit_*
Однако не указывать шаблон в кавычках опасно, потому что если (по какой-либо причине )существует файл, соответствующий --exclude=.*
, тогда шаблон расширяется:
$ touch -- --exclude=.special-filenames-happen
$./argtest.bash --exclude=.*
--exclude=.special-filenames-happen
Наконец, давайте посмотрим, почему использование массива предотвращает мою проблему с цитированием (в дополнение к другим преимуществам использования массивов для хранения аргументов команды ).
При определении массива разделение слов и обработка кавычек происходят, как и ожидалось:
$ ARRAY_OPTS=( -rnv --exclude='.*' )
$ echo length of the array: "${#ARRAY_OPTS[@]}"
length of the array: 2
$ echo first element: "${ARRAY_OPTS[0]}"
first element: -rnv
$ echo second element: "${ARRAY_OPTS[1]}"
second element: --exclude=.*
При передаче опций команде мы используем синтаксис "${ARRAY[@]}"
, который разворачивает каждый элемент массива в отдельное слово:
$./argtest.bash "${ARRAY_OPTS[@]}"
-rnv
--exclude=.*
Когда мы пишем функции и сценарии оболочки, в которых аргументы передаются для обработки, аргументы будут передаваться в численно -именованных переменных, например. 1 доллар, 2 доллара, 3 доллара
Например:
bash my_script.sh Hello 42 World
Внутри my_script.sh
команды будут использовать $1
для ссылки на Hello, $2
на 42
и $3
на World
Ссылка на переменную $0
будет расширена до имени текущего скрипта, например.my_script.sh
Не воспроизводите весь код с командами в качестве переменных.
Имейте в виду:
1 Избегайте использования в сценариях всех -имен переменных с заглавной буквы.
2 Не используйте обратные кавычки, вместо этого используйте $ (... ), это лучше вкладывается.
if [ $# -ne 2 ]
then
echo "Usage: $(basename $0) DIRECTORY BACKUP_DIRECTORY"
exit 1
fi
directory=$1
backup_directory=$2
current_date=$(date +%Y-%m-%dT%H-%M-%S)
backup_file="${backup_directory}/${current_date}.backup"
tar cv "$directory" | openssl des3 -salt | split -b 1024m - "$backup_file"