У меня есть сложная команда, которую я хотел бы сделать оболочкой / bash script. Я могу легко записать его в терминах $1
:
Я хочу иметь возможность передавать несколько имен ввода в script. Какой правильный способ сделать это?
И, конечно, я хочу обрабатывать имена файлов с пробелами в них.
Ответ 2
Перепишите теперь удаленный ответ VonC.
Роберт Гэмбл лаконичный ответ напрямую касается вопроса.
Это усиливает некоторые проблемы с именами файлов, содержащими пробелы.
Смотрите также: ${1: + "[email protected]" } в /bin/sh
Основной тезис: "[email protected]"
правильный, а $*
(без кавычек) почти всегда ошибочен.
Это связано с тем, что "[email protected]"
отлично работает, когда аргументы содержат пробелы и
работает так же, как $*
, когда они этого не делают.
В некоторых случаях "$*"
тоже ОК, но "[email protected]"
обычно (но не
всегда) работает в одних и тех же местах.
Без кавычек [email protected]
и $*
эквивалентны (и почти всегда ошибочны).
Итак, в чем разница между $*
, [email protected]
, "$*"
и "[email protected]"
? Все они связаны с "всеми аргументами оболочки", но они делают разные вещи. Если некорректно, $*
и [email protected]
делают то же самое. Они рассматривают каждое слово (последовательность не-пробелов) как отдельный аргумент. Котируемые формы совершенно разные, но: "$*"
рассматривает список аргументов как одну строку, разделенную пробелом, тогда как "[email protected]"
обрабатывает аргументы почти так же, как они были указаны в командной строке.
"[email protected]"
вообще не расширяется, когда нет позиционных аргументов; "$*"
расширяется до пустой строки — и да, есть разница, хотя это может быть трудно понять.
См. Дополнительную информацию ниже, после введения (нестандартной) команды al
.
Вторичный тезис:, если вам нужно обработать аргументы с пробелами, а затем
передавать их другим командам, тогда вам иногда требуется нестандартная
инструменты для оказания помощи. (Или вы должны использовать массивы, осторожно: "${array[@]}"
ведет себя аналогично "[email protected]"
.)
Пример:
$ mkdir "my dir" anotherdir
$ ls
anotherdir my dir
$ cp /dev/null "my dir/my file"
$ cp /dev/null "anotherdir/myfile"
$ ls -Fltr
total 0
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir/
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir/
$ ls -Fltr *
my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ ls -Fltr "./my dir" "./anotherdir"
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$ var='"./my dir" "./anotherdir"' && echo $var
"./my dir" "./anotherdir"
$ ls -Fltr $var
ls: "./anotherdir": No such file or directory
ls: "./my: No such file or directory
ls: dir": No such file or directory
$
Почему это не работает?
Это не работает, потому что оболочка обрабатывает кавычки перед ее расширением
переменные.
Итак, чтобы заставить оболочку обратить внимание на кавычки, встроенные в $var
,
вы должны использовать eval
:
$ eval ls -Fltr $var
./my dir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 my file
./anotherdir:
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 14:55 myfile
$
Это становится очень сложно, если у вас есть имена файлов, такие как "He said,
"Don't do this!"
" (с кавычками и двойными кавычками и пробелами).
$ cp /dev/null "He said, \"Don't do this!\""
$ ls
He said, "Don't do this!" anotherdir my dir
$ ls -l
total 0
-rw-r--r-- 1 jleffler staff 0 Nov 1 15:54 He said, "Don't do this!"
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 anotherdir
drwxr-xr-x 3 jleffler staff 102 Nov 1 14:55 my dir
$
Оболочки (все они) не делают особенно удобными для обработки таких
(так забавно), многие программы Unix не очень хорошо работают
обрабатывая их.
В Unix имя файла (один компонент) может содержать любые символы, кроме
косой чертой и NUL '\0'
.
Тем не менее, оболочки сильно не поощряют пробелов или новых строк или вкладок
в любом месте в именах путей.
Именно поэтому стандартные имена файлов Unix не содержат пробелов и т.д.
При использовании имен файлов, которые могут содержать пробелы и другие
вы должны быть очень осторожны, и я нашел
давно, что мне нужна была программа, которая не является стандартной для Unix.
Я называю это escape
(версия 1.1 датирована 1989-08-23T16: 01: 45Z).
Вот пример использования escape
- с системой управления SCCS.
Это обложка script, которая выполняет как delta
(думаю, заезд), так и
get
(думаю, выписка).
Различные аргументы, особенно -y
(причина, по которой вы внесли изменения)
будет содержать пробелы и новые строки.
Обратите внимание, что script датируется 1992 годом, поэтому он использует обратные тики вместо
$(cmd ...)
и не использует #!/bin/sh
в первой строке.
: "@(#)$Id: delget.sh,v 1.8 1992/12/29 10:46:21 jl Exp $"
#
# Delta and get files
# Uses escape to allow for all weird combinations of quotes in arguments
case `basename $0 .sh` in
deledit) eflag="-e";;
esac
sflag="-s"
for arg in "[email protected]"
do
case "$arg" in
-r*) gargs="$gargs `escape \"$arg\"`"
dargs="$dargs `escape \"$arg\"`"
;;
-e) gargs="$gargs `escape \"$arg\"`"
sflag=""
eflag=""
;;
-*) dargs="$dargs `escape \"$arg\"`"
;;
*) gargs="$gargs `escape \"$arg\"`"
dargs="$dargs `escape \"$arg\"`"
;;
esac
done
eval delta "$dargs" && eval get $eflag $sflag "$gargs"
(Я бы, вероятно, не использовал бы бегство настолько тщательно в эти дни - это
не требуется с аргументом -e
, например, но в целом это
один из моих более простых сценариев с использованием escape
.)
Программа escape
просто выводит свои аргументы, скорее, как echo
но это гарантирует, что аргументы защищены для использования с
eval
(один уровень eval
; у меня есть программа, которая сделала удаленную оболочку
выполнение, и это должно было избежать выхода escape
).
$ escape $var
'"./my' 'dir"' '"./anotherdir"'
$ escape "$var"
'"./my dir" "./anotherdir"'
$ escape x y z
x y z
$
У меня есть еще одна программа под названием al
, в которой перечислены ее аргументы по одному на строку
(и он еще более древний: версия 1.1 от 1987-01-27T14: 35: 49).
Это наиболее полезно при отладке скриптов, поскольку оно может быть подключено к
чтобы увидеть, какие аргументы фактически переданы команде.
$ echo "$var"
"./my dir" "./anotherdir"
$ al $var
"./my
dir"
"./anotherdir"
$ al "$var"
"./my dir" "./anotherdir"
$
[Добавлено:
И теперь, чтобы показать разницу между различными обозначениями "[email protected]"
, вот еще один пример:
$ cat xx.sh
set -x
al [email protected]
al $*
al "$*"
al "[email protected]"
$ sh xx.sh * */*
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al He said, '"Don'\''t' do 'this!"' anotherdir my dir xx.sh anotherdir/myfile my dir/my file
He
said,
"Don't
do
this!"
anotherdir
my
dir
xx.sh
anotherdir/myfile
my
dir/my
file
+ al 'He said, "Don'\''t do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file'
He said, "Don't do this!" anotherdir my dir xx.sh anotherdir/myfile my dir/my file
+ al 'He said, "Don'\''t do this!"' anotherdir 'my dir' xx.sh anotherdir/myfile 'my dir/my file'
He said, "Don't do this!"
anotherdir
my dir
xx.sh
anotherdir/myfile
my dir/my file
$
Обратите внимание: ничто не сохраняет исходные пробелы между *
и */*
в командной строке. Также обратите внимание, что вы можете изменить "аргументы командной строки" в оболочке, используя:
set -- -new -opt and "arg with space"
Это устанавливает 4 параметра: -new
',' -opt
',' and
'и' arg with space
'.
]
Хм, этот довольно длинный ответ - возможно, экзегеза - лучший термин.
Исходный код для escape
доступен по запросу (email to firstname dot
lastname в gmail dot com).
Исходный код для al
невероятно прост:
#include <stdio.h>
int main(int argc, char **argv)
{
while (*++argv != 0)
puts(*argv);
return(0);
}
Это все. Это эквивалентно test.sh
script, который показал Роберт Гэмбл и может быть записан как функция оболочки (но функции оболочки не существовали в локальной версии оболочки Bourne, когда я впервые написал al
).
Также обратите внимание, что вы можете написать al
как простую оболочку script:
[ $# != 0 ] && printf "%s\n" "[email protected]"
Условное значение необходимо, чтобы оно не выдавало вывода при отсутствии аргументов. Команда printf
создаст пустую строку с только аргументом строки формата, но программа C ничего не производит.