Если вы хотите сохранить порядок файла (не отсортированный), но все же удалить дубликаты, вы также можете сделать это
awk '!v[$1]++' /tmp/file
Например
d
d
a
a
b
b
c
c
c
c
c
Будет выведено
d
a
b
c
Судя по вашим комментариям к этому ответу, вы ожидаете, что в ответе будет сказано и признано, что парадигма zsh «лучше» и, следовательно, так должны работать все оболочки. "Лучше" - это просто мнение. Мнение не требует обсуждения.
Возможно, вы ожидаете узнать, почему $a
можно также использовать для массива.
Это то, что пытается сделать zsh, и даже если он проделал большую работу в этом направлении,
в некоторых случаях он все еще терпит неудачу. Он еще не копирует массивы ().
$ a=(1 2 3)
$ b=$a
$ printf '<%s>' $a ' ' $b; echo
<1><2><3>< ><1 2 3>
В нашем языке мы используем имя для каждой идеи. Каждая новая идея, которая радикально отличается от предыдущих идей , должна иметь собственное имя. Для нашего языка естественно, что мы принимаем новые слова для новых идей. Подумайте о названии «Интернет», новом имени для новой идеи. Использование старых названий для новых понятий всегда приводит к путанице и недопониманию. Вот как мы, люди, устроены, и звучит разумно, если подумать.
С самого началапеременная в оболочке использовала имя (accepta
)и ее значение было результатом расширения (процедуры оболочки )символов $a
. Переменная могла содержать строку или число (, автоматически преобразованное ).
Это именно то, что Perl сделал, используя @a
для обозначения списка , сохраняя при этом $a
для an scalar
.
То есть:Вышеупомянутые два называются контекстом SCALAR и LIST.(В Perl).
Вот почему у нас есть a=(1 2)
для назначения массива (есть и другие способы, но это наиболее распространенный ). Синтаксис, недопустимый в предыдущих оболочках.
В ш (в данном случае тире):
$ a=(12)
sh: 2: Syntax error: "(" unexpected
И расширение переменной было расширено с $a
или эквивалентного ${a}
на новый синтаксис (и недействительно в sh)`${a[@]}:
$ a=(aa bb cc)
$ printf '%s\n' "${a[@]}"
aa
bb
cc
В более простых оболочках (зола в данном случае):
$ a=Strin-Of-Text
$ printf '%s\n' "$a"
ash: syntax error: bad substitution
Очень жаль, что был выбран такой запутанный способ записи массива.
Если бы я предложил один новый синтаксис, я бы, вероятно, последовал примеру Perl и использовал что-то похожее на "@a" для списка значений (или, может быть, #a
или%a
). Это должно стать предметом некоторого обсуждения для достижения определенного консенсуса.
${a[@]}
. Короче :Для обратной совместимости ожидается, что раскрытие простой переменной $a
приводит только к одному значению, а не к списку значений, более того, к отдельным значениям.
Поскольку в POSIX не определены массивы, в -может быть целесообразно указать, что в POSIX определение Расширение параметра говорит:
The value, if any, of parameter shall be substituted.
И, ну,на самом деле, большинство оболочек печатают только скаляр с$a
bash(4.4) : <1><====><1><2><3>
lksh : <1><====><1><2><3>
mksh : <1><====><1><2><3>
ksh93 : <1><====><1><2><3>
astsh : <1><====><1><2><3>
zsh/ksh : <1><====><1><2><3>
zsh : <1><2><3><====><1><2><3>
Таким образом, для написания сценариев оболочки zsh [a] является нечетным.
Протестировано с этим скриптом:
a=(1 2 3)
printf '<%s>' $a '====';
printf '<%s>' "${a[@]}" ;
echo
[а] Тоже яш. csh-совместимые оболочки не тестировались. Скрипты с csh - известная проблема.
Я не могу дать ответ, но предлагаю несколько возможных объяснений.
Это правда, что кроме ksh и его клонов (pdksh и его производных и bash ), все остальные оболочки с массивами (csh
, tcsh
, rc
, es
, akanga
, fish
, zsh
,yash
)имеют $array
расширение на все элементы массива.
Но как в yash
, так и в zsh
(, когда в sh
эмуляции )две оболочки типа Bourne -в этом списке, это расширение по-прежнему подлежит разделению + подстановке (и по-прежнему пусто. удаление даже в zsh
даже если не в sh
эмуляции ), поэтому вам все равно нужно использовать неуклюжий синтаксис "${array[@]}"
(или "${(@)array}"
или "$array[@]"
в zsh
, которые вряд ли легче введите¹ )для сохранения списка(csh
и tcsh
имеют аналогичные проблемы ). Этот split+glob и пустое удаление являются наследием Борна (, в некоторой степени вызванным наследием оболочки Томпсона, где $1
больше похоже на макрорасширение ).
rc
и fish
— два примера более поздних оболочек, которые не имеют багажа Борна и имеют более чистый подход. Они признают тот факт, что оболочка является интерпретатором командной строки, и в первую очередь они имеют дело со списками (списка аргументов команд ),таким образом, список/массив является первичным типом данных (, существует только один тип, и это списки в rc
), и мы избавились от split+glob -после -ошибки расширения/неправильной функции оболочки Bourne (, которая больше не нужен, поскольку основным типом является массив ).
Тем не менее, это не объясняет, почему Дэвид Корн решил $array
расширить не все элементы, а элемент с индексом 0.
Теперь, за исключением csh
/tcsh
, все эти оболочки намного новее, чем ksh
, разработанные в начале 80-х, всего через несколько лет после выпуска оболочки Bourne и Unix V7. Unix V7 также представил среду. В то время это была модная новинка. Среда аккуратная и полезная, но переменные среды не могут содержать массивы, если вы не используете какую-либо форму кодирования.
Это всего лишь предположение, но я подозреваю, что одна из причин, по которой Дэвид Корн выбрал этот подход, заключалась в том, что интерфейс со средой не был изменен.
В ksh88, как и в rc, все переменные были (разреженными массивами; немного похоже на ассоциативные массивы с ключами, ограниченными положительными целыми числами, что является еще одной странностью по сравнению с другими оболочками или языками программирования, и вы можете сказать, что это не было полностью продумано, поскольку, например, было невозможно получить список ключей ). В этом новом дизайне var=value
стало сокращением от var[0]=value
. Вы по-прежнему можете экспортировать все свои переменные, но export var
экспортирует элемент индекса 0 массива в среду.
rc
помещает все свои переменные в среду, fish
поддерживает экспорт массивов, но для массивов с более чем одним элементом, (по крайней мере, для порта Unix для rc, который исходит от plan9 ), им приходится прибегать к какой-то форме кодирования, понятной только им.
csh
, tcsh
, zsh
не поддерживают экспорт массивов (, хотя в настоящее время это может не показаться большим ограничением ). Вы можете экспортировать массивы в yash
,но они экспортируются как переменная среды, которая представляет собой элементы массива, объединенные с :
(, поэтому (a "" "" b)
и (a : b)
экспортируются с одним и тем же значением ), и при импорте нет преобразования обратно в массив.
Другим возможным оправданием может быть согласованность с $@
/$*
(Борна, но тогда почему индексы массива начинаются с 0 вместо 1 (еще одна странность по сравнению с другими оболочками/языками того времени )? ). ksh
не было свободным ПО, это было коммерческое предприятие, одним из требований была совместимость по Борну. ksh
действительно удалил разделение полей, выполненное для каждого не -слова в кавычках в контексте списка (, поскольку это было явно бесполезно в оболочке Bourne ), но пришлось сохранить его для расширений (, так как скрипты использовали такие вещи, как var="file1 file2"; cmd $var
, поскольку оболочка Bourne не имела массива, кроме"$@"
). Сохранение этого в оболочке, которая в противном случае имеет массивы, не имеет особого смысла, но у Korn не было другого выбора, если Ksh все еще мог интерпретировать сценарии потребительской базы. Если бы $scalar
подвергалось разделению + подстановке, $array
должно было бы быть для согласованности, и поэтому "${array[@]}"
как обобщение "$@"
имело некоторый смысл. zsh
не имел подобного ограничения, поэтому мог свободно удалять split+glob при расширении одновременно с добавлением массивов (, но поплатился за нарушение обратной совместимости по Борну ).
Другим объяснением, предложенным @Arrow, могло быть то, что он не хотел перегружать существующие операторы, чтобы заставить их вести себя по-разному для разных типов переменных (например ${#var}
против ${#array}
через оболочку Bourne не было этого или ${var-value}
, ${var#pattern}
), что может вызвать путаницу у пользователей (в zsh
не всегда очевидно, как некоторые операторы работают с массивом против скаляра ).
Некоторая связанная литература:
ksh
(массивов, впервые добавленных в качестве исправлений поверх оболочки Bourne для системы ввода форм). Что касается случая a=$@
в вашем редактировании, это на самом деле один случай, когда ksh нарушил совместимость с оболочкой Bourne.
В оболочке Борна $@
и $*
содержали объединение позиционных параметров с символами пробела. Только $@
в кавычках был особенным, поскольку он расширялся до того же, что и "$*"
, но со вставленными пробелами, не заключенными в кавычки (с особыми случаями для пустого списка в более новых версиях, где к нему обращались, как в Solaris ). Вы заметите, что если вы удалите пробел из $IFS
, "$@"
расширяется до одного аргумента в контекстах списка (0 для пустого списка в исправленных версиях, упомянутых выше ). Если не цитировать, $*
и $@
ведут себя как любая другая переменная (, разделенная на символы $IFS
, не обязательно на исходные позиционные параметры ). Например, в оболочке Борна:
'set' 'a:b' 'c'
IFS=:
printf '<%s>\n' $@
printf '[%s]\n' "$@"
Будет вывод:
<a>
<b c>
[a:b c]
Ksh88 изменил это так, что $@
и $*
были объединены с первым символом $IFS
. "$@"
в контексте списка разделяет позиционные параметры, за исключением случаев, когда $IFS
пусто.
Когда $IFS
пусто, $*
соединяются через пробел, за исключением $*
в кавычках, которые соединяются без разделителя.
Примеры:
$ set a b
$ IFS=:
$ a=$@ b=$* c="$@" d="$*"
$ printf '<%s>\n' "$a" "$b" "$c" "$d" $@ $* "$@" "$*"
<a:b>
<a:b>
<a:b>
<a:b>
<a>
<b>
<a>
<b>
<a>
<b>
<a:b>
$ IFS=
$ a=$@ b=$* c="$@" d="$*"
$ printf '<%s>\n' "$a" "$b" "$c" "$d" $@ $* "$@" "$*"
<a b>
<a b>
<a b>
<ab>
<a b>
<a b>
<a b>
<ab>
Вы увидите множество вариаций в различных оболочках типа Bourne/Korn -, включая ksh93 и ksh88. Есть также некоторые вариации в таких случаях, как:
set --
cmd ''"$@"
cmd $empty"$@"
Или когда $IFS
содержит многобайтовые -символы или байты, не образующие допустимых символов.
¹ в yash
"$array"
ведет себя как "${array[@]}"
, а zsh
"$array"
ведет себя как "${array[*]}"
.Менее уродливый/неуклюжий, хотя, может быть, более удивительный.