Какой параметр в bash используется для подстановки, чтобы определить, соответствует ли * точечный файл

12

Недавно я удивился, когда сделал что-то подобное mv ./* ../somedirectory и обнаружил, что файлы вроде .gitignoreне были перемещены.

Я делаю большую часть своей работы в zsh на OS X, и этот сюрприз укусил меня в bash на CentOS. Я попытался bash на OS X и обнаружил то же поведение: *не совпадает с точечными файлами. Это кажется мне очень нежелательным, но, видимо, это bash default. (Это может быть тоже zsh по умолчанию, насколько я помню, но я мог изменить его много лет назад в моем .zshrc и забыл, что он когда-либо работал по-другому.)

Как настроить bash так, чтобы он вел себя так, как я ожидал: чтобы * соответствовал всем файлам, а не игнорировал точечные файлы.

Если это вообще неясно, вот как это воспроизвести

cd /tmp
mkdir {t,d}est
touch test/{.,}{1,2,3,4,5,6,7}
ls -hal test
mv test/* dest
ls -hal test     # notice dot files are still there
ls -hal dest     # notice only some files were mv'ed
иконоборец
источник
Возможный дубликат Как вы перемещаете все файлы (включая скрытые) в одном каталоге в другой?
Жиль "ТАК - перестань быть злым"
Да, они связаны. Это не обнаружилось, когда я искал (возможно, потому, что спрашивающий об этом не упомянул о глобализации или точечных файлах), но это действительно другой вопрос. Он спрашивал, как перемещать файлы, а я спрашивал, как изменить поведение оболочки, особенно для bash. Я мог бы не спросить, показался ли мне этот вопрос (поскольку ваш очень полный и исчерпывающий ответ включает настройку bash), но это все же другой вопрос, и тот, кто ищет ответ на мой вопрос, не обязательно найдет его вопрос.
иконоборчество
Весь этот бизнес «кто-то ищет ответ на мой вопрос не обязательно найдет его вопрос» - именно поэтому у нас есть такой способ закрытия вопросов как дубликатов.
Жиль "ТАК - перестань быть злым"
да, но вы, кажется, упускаете главное, что это другой вопрос .
иконоборчество

Ответы:

14

удар

Как вы уже заметили, bash не будет соответствовать a .в начале имени или косой черты. Чтобы изменить соответствие относительно точки, вы должны установить dotglobопцию - man bash :

dotglob Если установлено, bash включает имена файлов, начинающиеся с `. ' в
    результаты расширения пути.

Чтобы включить / настроить его с помощью bash shopt, например:

shopt -s dotglob


Для zsh вы также можете использовать эту dotglobопцию, но вы должны будете использовать setoptее, например:

setopt dotglob
Ульрих Дангел
источник
2
При zshэтом лучший вариант - включить dotglob для каждого отдельного глобуса :mv test/*(D) /dest
Стефан Шазелас
7

Я проверил это, и это решает проблему:

shopt -s dotglob

Выход:

~/stackexchangeanswers/40662$ ls -hal dest
total 8.0K
drwxr-xr-x 2 jodiec jodiec 4.0K 2012-06-12 22:15 .
drwxr-xr-x 4 jodiec jodiec 4.0K 2012-06-12 22:15 ..
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .1
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .2
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 3
-rw-r--r-- 1 jodiec jodiec    0 2012-06-12 22:15 .3
...snipped....
Джоди С
источник
Извините, но Ульрих ответил первым.
иконоборчество
3
Мы все в одной команде. Все нормально.
Джоди С
6

*это шар, который расширяется оболочкой. По умолчанию оболочки не включают файлы, имена которых начинаются с .(называемые скрытыми файлами или точечными файлами), если только начальный не .введен буквально.

*или [.]*или ?*или *.*или dir/*не будут включать точечные файлы.

.*или dir/.*будет.

Так что вы могли бы сделать:

mv -- * .* /dest/

однако некоторые оболочки, в том числе bash(но не zsh, mkshни fish), имеют ту ошибочную характеристику, что расширение .*включает в себя .и ..специальные записи каталога, что вам здесь не нужно (и, как правило, никогда не требует включения глобуса, поэтому я и называю это ошибкой).

По этой причине вы обнаружите, что иногда люди используют (в оболочках типа Борна):

mv -- * .[!.]* ..?* /dest/

Это три глобуса, первый из которых соответствует не скрытым файлам, второй - имена файлов, начинающиеся с .символа, за которым следует другой символ, .а третий - имена файлов, начинающиеся с ..символа, за которым следует хотя бы один символ.

Однако у некоторых современных оболочек есть лучшие способы обойти это

ЗШ

С помощью zshвы можете использовать (D)спецификатор glob, чтобы указать, что глобус должен включать точечные файлы:

mv -- *(D) /dest/

zshтакже исправлено другое несоответствие оболочки Bourne в том, что если шаблон не совпадает, mvкоманда не запускается.

Как сказано выше, он также никогда не будет включать .ни ..в свои глобусы, так

mv -- * .* /dest/

будет в безопасности. Однако, если нет соответствующего файла *или нет соответствующего файла, .*команда будет прервана, поэтому было бы лучше использовать:

mv -- (*|.*) /dest/

Как и в некоторых других оболочках, вы также можете заставить все глобусы включать точечные файлы (например, если вы хотите, чтобы точечные файлы включались чаще, чем нет) с помощью:

setopt dotglob

или же:

set -o dotglob

После этого, если вы хотите, чтобы определенный глобус не включал точечные файлы, вы можете написать это:

echo *(^D)

Или же:

echo [^.]*

удар

К сожалению bash, нет глобальных классификаторов. Таким образом, вы можете включить глобальное включение dotfile. В bashсинтаксис:

shopt -s dotglob

(и использовать [^.]*для шаров без скрытых файлов).

С dotglob, bashне включает .ни ..в глобусы, как *, но все же делает для глобусов, как .*.

Если вы установите GLOBIGNOREпеременную в непустое значение, она автоматически включит эту dotglobопцию и исключит .и ..из .*глобусов, но не из dir/.*или .*/fileиз (!), Так что защита практически бесполезна. Вы могли бы сделать, GLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*'но тогда это сломало бы шары как */.или ./*или ../*.

Лучшее решение - использовать [.]*или dir/[.]*или [.]*/filedotglobвключенным) для расширения файлов точек, кроме .и ...

рыбы

fishшары не включают .ни ... Когда нет совпадения, в зависимости от версии, он будет работать как zsh(или bash -o failglob) или bash -o nullglob.

mv -- * .* /dest/

Будет работать, если есть как скрытые, так и не скрытые файлы. В противном случае, YMMV и с некоторыми версиями, он может вызвать, mv -- /destесли нет файла вообще.

ksh93

Никаких глобус-классификаторов ksh93тоже нет. Вы можете включать дотфайлы в глобусы с помощью:

FIGNORE='@(.|..)'

В отличие от bashs GLOBIGNORE, это сделано правильно, а также устраняет проблему .*включения .и ...

йаш

yashимеет dot-globпараметр ( set -o dot-glob), но в отличие bashот глобальных расширений (даже *) включает, .и ..поэтому он довольно бесполезен.

Tcsh

set globdot

Работает как в bash, то есть *включает файлы точек, кроме .и, ..но .*все еще включает в себя .и ..(и вы можете использовать [.]*для расширения скрытых файлов, кроме .и ..).

Стефан Шазелас
источник
4

Самый простой способ в bash для печати соответствующих файлов точек, игнорируя .и ..это:

$ GLOBIGNORE=/+/
$ printf '%s\n' *


Тестировать:

$ cd tmp; mkdir empty; cd empty; touch {,.}{a..h}
$ GLOBIGNORE=/+/
$  ls *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

Он печатал все файлы с точками или без, с заметным исключением .и ...

Ваш пример также будет работать правильно:

$ cd /tmp; mkdir {t,d}est; touch test/{,.}{a..h}
$ mv test/* dest/
$ ls -a test
.  ..
$ ls -a test/*
ls: cannot access test/*: No such file or directory

Пусто файлов, которые имеют значение.
Системные файлы . ..все еще существуют, но расширение оболочки (используя *) не будет включать их.

И Dest каталог ination содержит все файлы:

$ cd dest
$ ls -a *
a  .a  b  .b  c  .c  d  .d  e  .e  f  .f  g  .g  h  .h

Почему?

Это работает, потому что настройка GLOBIGNORE имеет следующие побочные эффекты:

  • Установите dotglob ( shopt -s dotglob) для файлов математических точек в расширениях.
  • Имена файлов . и ..игнорируются, когда установлен GLOBIGNORE, а не NULL.

Подробности в руководстве: LESS=+'/The GLOBIGNORE' man bash.

Кроме того, нам нужно установить переменную GLOBIGNORE, которая будет содержать имя, которое не может совпадать ни с одним именем файла:
косая черта (и что-то еще в качестве двойной меры предосторожности).

$ GLOBIGNORE=/+/

Также может быть полезно установить nullglob, если это необходимо, чтобы избежать получения, *когда нет файла, соответствующего *.


источник
0

Очень интересует эта настройка GLOBIGNORE = / + / @ user79743

Из моего опыта снятия с охраны dotglob это плохой бизнес почти всех команд (ср, мв и т.д.) , но то же самое относится установка на nullglob (особенно с регулярными выражениями , которые затем нужно убегающих!).

Тем не менее, если вам нужно установить их в начале вашей программы, но вмешиваться в определенные части, где вы вызываете такие команды, как cp, mv и т. Д. Или используете регулярные выражения, просто сделайте это:

  # set dotglob, unset nullglob AFTER this
  is_nullglob=$( shopt -s | egrep -i '.*nullglob' )
  is_dotglob=$( shopt -s | egrep -i '.*dotglob' )
  [[ $is_nullglob ]] && shopt -u nullglob
  [[ ! $is_dotglob ]] && shopt -s dotglob
  # ... call commands ...
  # .....................
  # reset dotglob, nullglob to their previous values
  [[ $is_nullglob ]] && shopt -s nullglob
  [[ ! $is_dotglob ]] && shopt -u dotglob
Centurian
источник