Was ist die Einstellung in Bash für Globbing, um zu steuern, ob * mit Punktdateien übereinstimmt

12

Ich war kürzlich überrascht, als ich so etwas tat mv ./* ../somedirectory und feststellte, dass Dateien wie .gitignorenicht verschoben wurden.

Ich mache den größten Teil meiner Arbeit in zsh unter OS X, und diese Überraschung hat mich in CentOS verprügelt. Ich habe Bash unter OS X versucht und das gleiche Verhalten festgestellt: *Stimmt nicht mit Punktdateien überein. Dies scheint mir sehr unerwünscht zu sein, aber anscheinend ist es die Bash-Standardeinstellung. (Es ist vielleicht auch die zsh-Standardeinstellung, soweit ich mich erinnere, aber ich habe sie möglicherweise vor Jahren in meiner .zshrc geändert und vergessen, dass sie jemals anders funktioniert hat.)

Wie kann ich bash so konfigurieren, dass es sich wie erwartet verhält: damit * mit allen Dateien übereinstimmt und Punktdateien nicht ignoriert werden.

Falls dies überhaupt unklar ist, erfahren Sie hier, wie Sie es reproduzieren können

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
Bilderstürmer
quelle
Ja, sie sind verwandt. Das ist bei der Suche nicht aufgetaucht (wahrscheinlich, weil der Fragesteller keine Globbing- oder Punktedateien erwähnt hat), aber es ist wirklich eine andere Frage. Er fragte, wie man die Dateien verschiebt, und ich fragte, wie man das Shell-Verhalten ändert, und zwar speziell für Bash. Ich hätte vielleicht nicht gefragt, ob diese Frage für mich aufgetaucht ist (da Ihre äußerst vollständige und gründliche Antwort dort die Bash-Einstellung enthält), aber es ist immer noch eine andere Frage, und jemand, der nach einer Antwort auf meine Frage sucht, wird sie nicht unbedingt finden seine Frage.
Bilderstürmer
Das ganze Geschäft „Jemand, der nach einer Antwort auf meine Frage sucht, wird seine Frage nicht unbedingt finden“ ist genau der Grund, warum wir diese Art haben, Fragen als Duplikate zu schließen.
Gilles 'SO - hör auf böse zu sein'
Ja, aber Sie scheinen den Hauptpunkt zu verfehlen, nämlich dass es eine andere Frage ist .
Bilderstürmer

Antworten:

14

Bash

Wie Sie bereits bemerkt haben, stimmt Bash nicht mit einem .am Anfang des Namens oder einem Schrägstrich überein . Um die Übereinstimmung bezüglich des Punktes zu ändern, müssen Sie die dotglobOption - man bash :

dotglob Wenn gesetzt, enthält bash Dateinamen, die mit einem "." beginnen. im
    die Ergebnisse der Pfadnamenerweiterung.

Um es mit Bash zu aktivieren / einzustellen, verwenden Sie shoptzB:

shopt -s dotglob


Für zsh können Sie auch die dotglobOption verwenden, müssen sie jedoch verwenden setopt, um sie zu aktivieren, z.

setopt dotglob
Ulrich Dangel
quelle
2
Mit zshist die bessere Option, Dotglob pro Glob zu aktivieren :mv test/*(D) /dest
Stéphane Chazelas
7

Ich habe dies getestet und es löst das Problem:

shopt -s dotglob

Ausgabe:

~/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....
Jodie C.
quelle
Entschuldigung, aber Ulrich hat zuerst geantwortet.
Bilderstürmer
3
Wir sind alle im selben Team. Es ist in Ordnung.
Jodie C
6

*ist ein Glob, der von der Shell erweitert wird. Standardmäßig enthalten Shells keine Dateien, deren Name mit einem .(als versteckte Dateien oder Punktdateien bezeichnet) beginnt, es sei denn, der führende .wird wörtlich eingegeben.

*oder [.]*oder ?*oder *.*oder dir/*wird keine Punktdateien enthalten.

.*oder dir/.*wird.

So könnten Sie tun:

mv -- * .* /dest/

jedoch einige Schalen einschließlich bash(aber nicht zsh, mkshnoch fish) haben , dass misfeature dass die Ausweitung der .*die umfassen .und ..spezielle Verzeichniseinträge, die Sie nicht wollen , hier (und in der Regel nie eine glob aufnehmen möchten, weshalb ich es nennen misfeature).

Aus diesem Grund werden Sie feststellen, dass Menschen manchmal (in Bourne-ähnlichen Muscheln) verwenden:

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

Das sind drei Globs, wobei der erste mit nicht versteckten Dateien übereinstimmt, der zweite mit Dateinamen beginnt, .gefolgt von einem anderen Zeichen als .und der dritte mit Dateinamen, ..gefolgt von mindestens einem Zeichen.

Einige moderne Muscheln haben jedoch bessere Möglichkeiten, dies zu umgehen

zsh

Mit zshkönnen Sie das (D)Glob-Qualifikationsmerkmal verwenden, um anzugeben, dass der Glob Punktdateien enthalten soll:

mv -- *(D) /dest/

zshEs wurde auch behoben, dass andere Fehlfunktionen der Bourne-Shell darin bestehen, dass der mvBefehl nicht ausgeführt wird , wenn das Muster nicht übereinstimmt .

Wie oben gesagt wird es auch nie gehört .noch ..in seiner Kleckse, so

mv -- * .* /dest/

wird sicher sein. Wenn jedoch keine Datei *oder keine Datei übereinstimmt, wird .*der Befehl abgebrochen. Daher ist es besser, Folgendes zu verwenden:

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

Wie in einigen anderen Shells können Sie auch alle Globs zwingen, Dotfiles einzuschließen (z. B. wenn Sie möchten, dass Dotfiles häufiger enthalten sind):

setopt dotglob

oder:

set -o dotglob

Wenn Sie danach möchten, dass ein bestimmter Glob keine Punktdateien enthält, können Sie ihn schreiben:

echo *(^D)

Oder:

echo [^.]*

Bash

Leider bashgibt es keine Glob-Qualifikation. Sie müssen also die Dotfile-Aufnahme global aktivieren. In bashlautet die Syntax:

shopt -s dotglob

(und [^.]*für Globs ohne versteckte Dateien verwenden).

Mit dotglob, bashnicht enthalten .noch ..in Globs wie *, aber immer noch für Globs wie .*.

Wenn Sie die GLOBIGNOREVariable auf einen nicht leeren Wert setzen, wird die dotglobOption automatisch aktiviert und es werden .und ..von .*Globs ausgeschlossen, jedoch nicht von dir/.*oder .*/fileone (!), So dass der Schutz ziemlich nutzlos ist. Sie könnten tun, GLOBIGNORE='*/.:*/..:./*:../*:*/./*:*/../*'aber dann würde es Globs wie */.oder ./*oder brechen ../*.

Eine bessere Lösung besteht darin, Dotfiles mit Ausnahme von und [.]*oder dir/[.]*oder [.]*/file(mit dotglobaktiviertem) zu erweitern ....

Fisch

fishGlobs enthalten weder .noch ... Wenn es je nach Version keine Übereinstimmung gibt, funktioniert es entweder zsh(oder bash -o failglob) oder bash -o nullglob.

mv -- * .* /dest/

Würde funktionieren, wenn es sowohl versteckte als auch nicht versteckte Dateien gibt. Andernfalls wird YMMV und bei einigen Versionen möglicherweise aufgerufen, mv -- /destwenn überhaupt keine Datei vorhanden ist.

ksh93

Auch kein Glob-Qualifier ksh93. Sie können Punktedateien in Globs einfügen mit:

FIGNORE='@(.|..)'

Im Gegensatz zu bash‚s GLOBIGNORE, die richtig und auch getan behebt das Problem , .*einschließlich .und ...

yash

yashhat eine dot-globOption ( set -o dot-glob), aber im Gegensatz dazu enthalten bashdie Glob-Erweiterungen (sogar von *) .und ist ..daher ziemlich nutzlos.

tcsh

set globdot

Funktioniert wie in bash, *dh enthält Punktdateien mit Ausnahme von .und enthält ..jedoch .*weiterhin .und ..(und Sie können [.]*versteckte Dateien mit Ausnahme von .und erweitern ..).

Stéphane Chazelas
quelle
4

Der einfachste Weg in Bash, passende Punktdateien zu drucken, zu ignorieren .und ..ist:

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


Zu testen:

$ 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

Es wurden alle Dateien mit oder ohne Punkte gedruckt, mit Ausnahme von .und ...

Ihr Beispiel wird auch richtig funktionieren:

$ 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

Leeren Sie wichtige Dateien.
Die Systemdateien sind . ..noch vorhanden, werden jedoch von einer Shell-Erweiterung (mit *) nicht berücksichtigt.

Und das dest ination Verzeichnis enthält alle Dateien:

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

Warum?

Dies funktioniert , weil Einstellung GLOBIGNORE Dies hat Nebenwirkungen:

  • Setzen Sie dotglob ( shopt -s dotglob) auf mathematische Punktdateien für Erweiterungen.
  • Dateinamen . und ..werden ignoriert, wenn GLOBIGNORE gesetzt und nicht null ist.

Details im Handbuch : LESS=+'/The GLOBIGNORE' man bash.

Außerdem müssen wir die Variable GLOBIGNORE so einstellen, dass sie einen Namen enthält, mit dem kein Dateiname übereinstimmen kann:
einen Schrägstrich (und etwas anderes, nur als doppelte Vorsichtsmaßnahme).

$ GLOBIGNORE=/+/

Es kann nützlich sein, auch nullglob festzulegen, wenn dies erforderlich ist, um zu vermeiden, dass eine *Datei angezeigt wird, wenn keine entsprechende Datei vorhanden ist *.


quelle
0

Sehr interessiert diese Einstellung von GLOBIGNORE = / + / @ user79743

Aus meiner Erfahrung Entschärfen ist dotglob schlechtes Geschäft für fast alle Ihre Befehle (cp, mv usw.) , aber das gleiche gilt Einstellung die nullglob (vor allem mit regulären Ausdrücken , die dann brauchen Flucht!).

Wenn Sie sie jedoch zu Beginn Ihres Programms festlegen müssen, aber in bestimmte Bereiche eingreifen müssen, in denen Sie Befehle wie cp, mv usw. aufrufen oder reguläre Ausdrücke verwenden, gehen Sie wie folgt vor:

  # 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
quelle