it-swarm.com.de

Testen Sie, ob Dateien vorhanden sind, die einem Muster entsprechen, um ein Skript auszuführen

Ich versuche, eine if -Anweisung zu schreiben, um zu testen, ob Dateien vorhanden sind, die einem bestimmten Muster entsprechen. Wenn sich in einem Verzeichnis eine Textdatei befindet, sollte ein bestimmtes Skript ausgeführt werden.

Mein Code derzeit:

if [ -f /*.txt ]; then ./script fi

Bitte geben Sie einige Ideen; Ich möchte das Skript nur ausführen, wenn ein .txt im Verzeichnis.

29
user40952
[ -f /*.txt ]

würde nur dann true zurückgeben, wenn eine (und nur eine) nicht versteckte Datei in / vorhanden ist, deren Name auf .txt endet und wenn diese Datei eine reguläre Datei oder eine ist Symlink zu einer regulären Datei.

Dies liegt daran, dass Platzhalter von der Shell erweitert werden, bevor sie an den Befehl übergeben werden (hier [).

Wenn es also einen /a.txt Und /b.txt Gibt, werden [ 5 Argumente übergeben: [, -f, /a.txt , /b.txt Und ]. [ Würde sich dann beschweren, dass -f Zu viele Argumente erhält.

Wenn Sie überprüfen möchten, ob das Muster *.txt Auf mindestens eine nicht ausgeblendete Datei (regulär oder nicht) erweitert wird:

shopt -s nullglob
set -- *.txt
if [ "$#" -gt 0 ]; then
  ./script "[email protected]" # call script with that list of files.
fi
# Or with bash arrays so you can keep the arguments:
files=( *.txt )
# apply C-style boolean on member count
(( ${#files[@]} )) && ./script "${files[@]}"

shopt -s nullglob Ist bash spezifisch, aber Shells wie ksh93, zsh, yash, tcsh haben äquivalente Anweisungen.

Beachten Sie, dass es diese Dateien durch Lesen des Inhalts des Verzeichnisses findet und überhaupt nicht versucht, auf diese Dateien zuzugreifen, was es effizienter macht als Lösungen, die Befehle wie ls oder stat on aufrufen diese Liste der von der Shell berechneten Dateien.

Das Standardäquivalent sh wäre:

set -- [*].txt *.txt
case "$1$2" in
  ('[*].txt*.txt') ;;
  (*) shift; script "[email protected]"
esac

Das Problem ist, dass bei Bourne- oder POSIX-Shells ein Muster, das nicht übereinstimmt, zu sich selbst erweitert wird. Wenn also *.txt Auf *.txt Erweitert wird, wissen Sie nicht, ob es daran liegt, dass sich keine .txt - Datei im Verzeichnis befindet oder dass es eine Datei mit dem Namen *.txt Gibt. Die Verwendung von [*].txt *.txt Ermöglicht die Unterscheidung zwischen beiden.

39

Sie können immer find verwenden:

find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script

Erläuterung:

  • find .: Durchsucht das aktuelle Verzeichnis
  • -maxdepth 1: Unterverzeichnisse nicht durchsuchen
  • -type f: Suche nur nach regulären Dateien
  • name "*.txt": Suche nach Dateien mit der Endung .txt
  • 2>/dev/null: Fehlermeldungen umleiten an /dev/null
  • | grep -q .: grep für ein beliebiges Zeichen, gibt false zurück, wenn keine Zeichen gefunden wurden.
  • && ./script : Ausführen ./script nur wenn der vorherige Befehl erfolgreich war ( && )
11
terdon

Eine mögliche Lösung ist auch Bash Builtin compgen. Dieser Befehl gibt alle möglichen Übereinstimmungen für ein Globbing-Muster zurück und verfügt über einen Exit-Code, der angibt, ob Dateien übereinstimmen.

compgen -G "/*.text" > /dev/null && ./script

Ich habe diese Frage gefunden, als ich nach Lösungen gesucht habe, die jedoch schneller sind.

8
Daniel Böhmer

Hier ist ein Einzeiler, um es zu tun:

$ ls
file1.pl  file2.pl

dateien existieren

$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists

dateien existieren nicht

$ stat -t -- *.txt >/dev/null 2>&1 && echo "file exists" || echo "file don't exist"
file don't exist

Dieser Ansatz verwendet die Operatoren || Und && In bash. Dies sind die Operatoren "oder" und "und".

Wenn der stat-Befehl also ein $? Gleich 0 zurückgibt, wird das erste echo aufgerufen, wenn er eine 1 zurückgibt, wird das zweite echo aufgerufen.

ergebnisse von stat zurückgeben

# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1

# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0

Diese Frage wird im Stackoverflow ausführlich behandelt:

7
slm

Wie Chazelas betont, würde Ihr Skript fehlschlagen, wenn die Platzhaltererweiterung mit mehr als einer Datei übereinstimmt.

Es gibt jedoch einen Trick, den ich benutze (auch wenn ich ihn nicht sehr mag), um herumzukommen:

PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi

Wie es funktioniert?

Die Platzhaltererweiterung stimmt mit einem Array von Dateinamen überein. Wenn einige vorhanden sind, erhalten wir den ersten, andernfalls null, wenn keine Übereinstimmung vorliegt.

4
Pei Z

Einfach wie:

cnt=`ls \*.txt 2>/dev/null | wc -l`
if [ "$cnt" != "0" ]; then ./script fi

wc -l zählt die Zeilen im erweiterten Platzhalter.

1
Kim Johansson

Ich mag die vorherige Array-Lösung, aber das könnte bei einer großen Anzahl von Dateien verschwenderisch werden - die Shell würde viel Speicher zum Erstellen des Arrays verwenden, und nur das erste Element würde jemals getestet werden.

Hier ist eine alternative Struktur, die ich gestern getestet habe:

$ cd /etc; if [[ $(echo * | grep passwd) ]];then echo yes;else echo no;fi yes $ cd /etc; if [[ $(echo * | grep password) ]];then echo yes;else echo no;fi no

Der Exit-Wert aus dem grep scheint den Pfad durch die Kontrollstruktur zu bestimmen. Dies wird auch mit regulären Ausdrücken anstelle von Shell-Mustern getestet. Einige meiner Systeme haben den Befehl "pcregrep", der viel komplexere Regex-Übereinstimmungen ermöglicht.

(Ich habe diese Antwort bearbeitet, um ein "ls" in der Befehlsersetzung zu entfernen, nachdem ich die obige Kritik zum Parsen gelesen habe.)

0
Charlie