Яке налаштування в bash для глобулювання, щоб контролювати відповідність * точкових файлів


12

Нещодавно я здивувався, коли зробив щось на кшталт mv ./* ../somedirectory і виявив, що файли на зразок .gitignoreне переміщені.

Більшу частину своєї роботи я роблю в zsh на OS X, і ця несподіваність мене покусала в башті на CentOS. Я спробував bash на OS X і виявив таку ж поведінку: *не відповідає точковим файлам. Це здається мені дуже небажаним, але, мабуть, це башти за замовчуванням. (Це може бути також 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. Я, можливо, не запитав, чи з’явилося це запитання для мене (оскільки ваша надзвичайно повна і ретельна відповідь містить налаштування баш), але це все ж інше питання, і хтось, хто шукає відповідь на моє запитання, не обов'язково знайде його питання.
іконоборство

Весь бізнес «хто шукає відповідь на моє запитання, не обов'язково знайде своє питання» саме тому ми маємо такий спосіб закриття запитань як дублікати.
Жиль 'ТАК - перестань бути злим'

так, але, здається, вам не вистачає головного моменту, а це те, що це інше питання .
іконоборство

Відповіді:


14

Баш

Як ви вже помітили, bash не буде відповідати а .на початку імені або косою рисою. Щоб змінити відповідність точки, вам потрібно встановити dotglobпараметр - man bash :

dotglob Якщо встановлено, bash включає в себе назви файлів, що починаються з "." в
    результати розширення імені шляху.

Щоб увімкнути / встановити його з використанням bash shopt, наприклад:

shopt -s dotglob


Для zsh ви також можете скористатися dotglobопцією, але вам доведеться використовувати setoptїї, наприклад:

setopt dotglob

2
З zsh, найкращим варіантом є надання dotglob на основі кожного Глоб:mv test/*(D) /dest
Stéphane Chazelas

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

*являє собою глобус, розширений оболонкою. За замовчуванням оболонки не включають файли, ім'я яких починається з .(називаються приховані файли або dotfiles), якщо ведучі .не вводяться буквально.

*або [.]*або ?*або *.*або 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

Після цього, якщо ви хочете, щоб певний глобус не включав dotfiles, ви можете написати його:

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якщо файл взагалі не існує.

кш93

Жоден глобальний класифікатор ksh93жоден. Ви можете включити точкові файли в глобуси за допомогою:

FIGNORE='@(.|..)'

На відміну від bashs GLOBIGNORE, це зроблено належним чином, а також вирішує проблему .*включення .та ...

яш

yashмає dot-globопцію ( set -o dot-glob), але, всупереч bash, розширення (навіть *) глобальної сфери включають, .і ..тому це досить марно.

тч

set globdot

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


4

Найпростіший спосіб в Баш друкувати відповідні точкові файли, НЕ звертаючи уваги .і ..є:

$ 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 встановлюється і не нуль.

Подробиці в керівництві: 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.