webentwicklung-frage-antwort-db.com.de

Testen Sie, ob ein Glob in bash Übereinstimmungen aufweist

Wenn ich das Vorhandensein einer einzelnen Datei überprüfen möchte, kann ich sie mit test -e filename oder [ -e filename ] testen.

Angenommen, ich habe einen Glob und möchte wissen, ob Dateien existieren, deren Namen mit dem Glob übereinstimmen. Der Glob kann mit 0 Dateien übereinstimmen (in diesem Fall muss ich nichts tun) oder er kann mit einer oder mehreren Dateien übereinstimmen (in diesem Fall muss ich etwas tun). Wie kann ich testen, ob ein Glob eine Übereinstimmung hat? (Es ist mir egal, wie viele Übereinstimmungen es gibt, und es wäre am besten, wenn ich dies mit einer if-Anweisung und ohne Schleifen tun könnte (einfach weil ich das am besten lesbar finde).

(test -e glob* schlägt fehl, wenn der Glob mit mehr als einer Datei übereinstimmt.)

184
Ken Bloom

Bash spezifische Lösung:

compgen -G "<glob-pattern>"

Entkomme dem Muster oder es wird in Übereinstimmungen erweitert. 

Ausgangsstatus ist:

  • 1 für kein Spiel 
  • 0 für 'ein oder mehrere Treffer'

stdout ist eine Liste von Dateien, die mit dem Glob übereinstimmen.
Ich denke, dies ist die beste Option in Bezug auf Prägnanz und die Minimierung möglicher Nebenwirkungen.

UPDATE: Beispielnutzung angefordert.

if compgen -G "/tmp/someFiles*" > /dev/null; then
    echo "Some files exist."
fi
127
Brian Chrisman

Die Shell-Option von nullglob ist in der Tat ein Bashmus.

Um das langwierige Speichern und Wiederherstellen des Nullglob-Status zu vermeiden, würde ich ihn nur in die Subshell setzen, die den Glob erweitert:

if test -n "$(shopt -s nullglob; echo glob*)"
then
    echo found
else
    echo not found
fi

Verwenden Sie find für eine bessere Portabilität und flexibleres Globbing:

if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
    echo found
else
    echo not found
fi

Explizite -print -quit -Aktionen werden für find anstelle der standardmäßigen impliziten -print -Aktion verwendet, sodass find beendet wird, sobald die erste Datei gefunden wird, die der Suche entspricht Kriterien. Wenn viele Dateien übereinstimmen, sollte dies viel schneller als echo glob* oder ls glob* ausgeführt werden, und es wird auch die Möglichkeit vermieden, die erweiterte Befehlszeile zu überfüllen (einige Shells haben ein Längenlimit von 4K).

Wenn sich find als übertrieben anfühlt und die Anzahl der Dateien, die wahrscheinlich übereinstimmen, gering ist, verwenden Sie stat:

if stat -t glob* >/dev/null 2>&1
then
    echo found
else
    echo not found
fi
148
flabdablet
#!/usr/bin/env bash

# If it is set, then an unmatched glob is swept away entirely -- 
# replaced with a set of zero words -- 
# instead of remaining in place as a single Word.
shopt -s nullglob

M=(*px)

if [ "${#M[*]}" -ge 1 ]; then
    echo "${#M[*]} matches."
else
    echo "No such files."
fi
20
miku

Ich mag

exists() {
    [ -e "$1" ]
}

if exists glob*; then
    echo found
else
    echo not found
fi

Dies ist sowohl lesbar als auch effizient (sofern nicht sehr viele Dateien vorhanden sind).
Der Hauptnachteil ist, dass es viel subtiler ist, als es aussieht, und ich fühle mich manchmal gezwungen, einen langen Kommentar hinzuzufügen.
Wenn es eine Übereinstimmung gibt, wird "glob*" von der Shell erweitert und alle Übereinstimmungen werden an exists() übergeben, die die erste prüft und den Rest ignoriert.
Wenn es keine Übereinstimmung gibt, wird "glob*" an exists() übergeben und dort nicht vorhanden gefunden.

Edit: es kann ein falsch positives Ergebnis geben, siehe comment

16
Dan Bloch

test -e hat den unglücklichen Vorbehalt, dass gebrochene symbolische Links als nicht vorhanden betrachtet werden. Vielleicht möchten Sie auch nach diesen suchen. 

function globexists {
  test -e "$1" -o -L "$1"
}

if globexists glob*; then
    echo found
else
    echo not found
fi
6
NerdMachine

Wenn du GlobFail gesetzt hast, kannst du dieses verrückte verwenden (was du eigentlich nicht solltest)

shopt -s failglob # exit if * does not match 
( : * ) && echo 0 || echo 1

oder 

q=( * ) && echo 0 || echo 1
4
Arcabard

Ich habe noch eine andere Lösung:

if [ "$(echo glob*)" != 'glob*' ]

Das funktioniert gut für mich. Gibt es einige Eckfälle, die ich vermisse?

4
SaschaZorn

Basierend auf der Antwort von flabdablet scheint es für mich am einfachsten (nicht unbedingt am schnellsten), find selbst zu verwenden, während die Glob-Erweiterung auf Shell verbleibt, wie:

find /some/{p,long-p}ath/with/*globs* -quit &> /dev/null && echo "MATCH"

Oder in if wie:

if find $yourGlob -quit &> /dev/null; then
    echo "MATCH"
else
    echo "NOT-FOUND"
fi
3
queria

Um die Antwort von MYYN etwas zu vereinfachen, basierend auf seiner Idee:

M=(*py)
if [ -e ${M[0]} ]; then
  echo Found
else
  echo Not Found
fi
3
Ken Bloom

Dieser Greuel scheint zu funktionieren:

#!/usr/bin/env bash
shopt -s nullglob
if [ "`echo *py`" != "" ]; then
    echo "Glob matched"
else
    echo "Glob did not match"
fi

Es erfordert wahrscheinlich bash, nicht sh.

Dies funktioniert, weil die Option nullglob bewirkt, dass der Glob als leere Zeichenfolge ausgewertet wird, wenn keine Übereinstimmungen vorhanden sind. Daher zeigt jede nicht leere Ausgabe des Echobefehls an, dass der Glob etwas stimmte.

1
Ryan Thompson

In Bash können Sie zu einem Array globieren. Wenn der Glob nicht übereinstimmt, enthält Ihr Array einen einzelnen Eintrag, der keiner vorhandenen Datei entspricht:

#!/bin/bash

shellglob='*.sh'

scripts=($shellglob)

if [ -e "${scripts[0]}" ]
then stat "${scripts[@]}"
fi

Hinweis: Wenn Sie nullglob eingestellt haben, ist scripts ein leeres Array, und Sie sollten stattdessen mit [ "${scripts[*]}" ] oder mit [ "${#scripts[*]}" != 0 ] testen. Wenn Sie eine Bibliothek schreiben, die mit oder ohne nullglob funktionieren muss, müssen Sie dies tun

if [ "${scripts[*]}" ] && [ -e "${scripts[0]}" ]

Ein Vorteil dieses Ansatzes besteht darin, dass Sie über die Liste der Dateien verfügen, mit denen Sie arbeiten möchten, anstatt die Glob-Operation wiederholen zu müssen.

1
Toby Speight

Ich habe diese Antwort nicht gesehen.

set -- glob*
[ -f "$1" ] && echo "found [email protected]"
1
Brad Howes
#!/bin/bash
set nullglob
touch /tmp/foo1 /tmp/foo2 /tmp/foo3
FOUND=0
for FILE in /tmp/foo*
do
    FOUND=$((${FOUND} + 1))
done
if [ ${FOUND} -gt 0 ]; then
    echo "I found ${FOUND} matches"
else
    echo "No matches found"
fi
0
Peter Lyons
if ls -d $glob > /dev/null 2>&1; then
  echo Found.
else
  echo Not found.
fi

Beachten Sie, dass dies sehr zeitaufwändig sein kann, wenn viele Übereinstimmungen vorhanden sind oder der Dateizugriff langsam ist.

0
Florian Diesch

[ls glob* 2>/dev/null | head -n 1] && echo true

0
otocan
set -- glob*
if [ -f "$1" ]; then
  echo "It matched"
fi

Erläuterung

Wenn keine Übereinstimmung für glob* vorhanden ist, enthält $1'glob*'. Der Test -f "$1" ist nicht wahr, da die glob*-Datei nicht vorhanden ist.

Warum das besser ist als Alternativen

Dies funktioniert mit sh und Derivaten: ksh und bash. Es erstellt keine Sub-Shell. $(..)- und `...`-Befehle erstellen eine Sub-Shell. Sie verzweigen einen Prozess und sind daher langsamer als diese Lösung.

0
joseyluis