Переписать теперь удаленный ответ по VonC .
Сжатый ответ Роберта Гэмбла касается непосредственно вопроса.
Это усиливает некоторые проблемы с именами файлов, содержащими пробелы.
См. Также: $ {1: + "$ @"} в / bin / sh
Основной тезис: "$@"
правильно, а $*
(без кавычек) почти всегда неправильно.
Это потому, что "$@"
работает нормально, когда аргументы содержат пробелы, и
работает так же, как $*
, когда они этого не делают.
В некоторых случаях "$*"
тоже нормально, но обычно "$@"
(но не
всегда) работает в одних и тех же местах.
Без кавычек $@
и $*
эквивалентны (и почти всегда неверны).
Итак, в чем разница между $*
, $@
, "$*"
и "$@"
? Все они связаны со «всеми аргументами оболочки», но делают разные вещи. В кавычках $*
и $@
делают то же самое. Они рассматривают каждое слово (последовательность непробельных символов) как отдельный аргумент. Однако формы в кавычках совершенно разные: "$*"
обрабатывает список аргументов как одну строку, разделенную пробелами, тогда как "$@"
обрабатывает аргументы почти точно так же, как они были указаны в командной строке.
"$@"
вообще ничего не расширяется, когда нет позиционных аргументов; "$*"
раскрывается в пустую строку & mdash; и да, есть разница, хотя это может быть трудно понять.
См. Дополнительную информацию ниже, после введения (нестандартной) команды al
.
Вторичный тезис: если вам нужно обработать аргументы с пробелами, а затем
передать их другим командам, тогда вам иногда нужно нестандартное
инструменты для оказания помощи. (Или вы должны использовать массивы, осторожно: "${array[@]}"
ведет себя аналогично "$@"
.)
* +1047 *
Пример: * 1 049 *
$ 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.
Это сценарий обложки, который выполняет как delta
(думаю check-in ), так и
get
(думаю выезд ).
Различные аргументы, особенно -y
(причина, по которой вы внесли изменения)
будет содержать пробелы и новые строки.
Обратите внимание, что сценарий датируется 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 "$@"
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"
$
[ Добавлено:
А теперь, чтобы показать разницу между различными обозначениями "$@"
, приведем еще один пример:
$ cat xx.sh
set -x
al $@
al $*
al "$*"
al "$@"
$ 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
'.
]
Хм, это довольно длинный ответ - возможно, exegesis - лучший термин.
Исходный код escape
доступен по запросу (электронная почта на имя, точка
фамилия в gmail точка ком).
Исходный код al
невероятно прост:
#include <stdio.h>
int main(int argc, char **argv)
{
while (*++argv != 0)
puts(*argv);
return(0);
}
Вот и все. Он эквивалентен сценарию test.sh
, который показал Роберт Гэмбл, и может быть написан как функция оболочки (но функции оболочки не существовали в локальной версии оболочки Bourne, когда я впервые написал al
).
Также обратите внимание, что вы можете написать al
в виде простого сценария оболочки:
[ $# != 0 ] && printf "%s\n" "$@"
Условное условие необходимо для того, чтобы оно не выдавало выходных данных при отсутствии аргументов. Команда printf
выдаст пустую строку только с аргументом строки формата, но программа на Си ничего не выдаст.