it-swarm.com.de

Bash-Funktion, um das neueste Muster für die Dateiübereinstimmung zu finden

In Bash möchte ich eine Funktion erstellen, die den Dateinamen der neuesten Datei zurückgibt, die einem bestimmten Muster entspricht. Zum Beispiel habe ich ein Verzeichnis mit Dateien wie:

Directory/
   a1.1_5_1
   a1.2_1_4
   b2.1_0
   b2.2_3_4
   b2.3_2_0

Ich möchte die neueste Datei, die mit 'b2' beginnt. Wie mache ich das in bash? Ich muss dies in meinem ~/.bash_profile-Skript haben.

100
jlconlin

Der Befehl ls hat einen Parameter -t zum Sortieren nach Zeit. Sie können dann die erste (neueste) mit head -1 abholen.

ls -t b2* | head -1

Aber Vorsicht: Warum Sie die Ausgabe von ls nicht analysieren sollten

Meine persönliche Meinung: Das Analysieren von ls ist nur dann gefährlich, wenn die Dateinamen lustige Zeichen wie Leerzeichen oder Zeilenumbrüche enthalten können. Wenn Sie garantieren können, dass die Dateinamen keine lustigen Zeichen enthalten, ist die Analyse von ls ziemlich sicher.

Wenn Sie ein Skript entwickeln, das von vielen Benutzern auf vielen Systemen in vielen verschiedenen Situationen ausgeführt werden soll, empfehle ich Ihnen dringend, ls nicht zu analysieren.

So machen Sie es "richtig": Wie finde ich die neueste (neueste, früheste, älteste) Datei in einem Verzeichnis?

unset -v latest
for file in "$dir"/*; do
  [[ $file -nt $latest ]] && latest=$file
done
166
lesmana

Die Kombination von find und ls funktioniert gut für

  • dateinamen ohne Zeilenumbrüche
  • nicht sehr viele Dateien
  • nicht sehr lange Dateinamen

Die Lösung:

find . -name "my-pattern" ... -print0 |
    xargs -0 ls -1 -t |
    head -1

Lass es uns aufschlüsseln:

Mit find können wir alle interessanten Dateien wie folgt abgleichen:

find . -name "my-pattern" ...

mit -print0 können wir dann alle Dateinamen sicher an die ls übergeben:

find . -name "my-pattern" ... -print0 | xargs -0 ls -1 -t

ls -t sortiert die Dateien nach der Änderungszeit (neueste zuerst) und druckt sie in einer Zeile aus. Sie können -c zum Sortieren nach Erstellungszeitpunkt verwenden. Hinweis : Dies wird mit Dateinamen unterbrochen, die Zeilenumbrüche enthalten.

Schließlich erhält head -1 die erste Datei in der sortierten Liste.

Hinweis: xargs verwendet Systemgrenzen für die Größe der Argumentliste. Wenn diese Größe überschritten wird, wird xargs mehrmals ls aufgerufen. Dadurch wird die Sortierung und wahrscheinlich auch die endgültige Ausgabe unterbrochen. Lauf 

xargs  --show-limits

um die Grenzen Ihres Systems zu überprüfen.

Hinweis 2: Verwenden Sie find . -maxdepth 1 -name "my-pattern" -print0, wenn Sie Dateien nicht durch Unterordner durchsuchen möchten.

9
Boris Brodski

Dies ist eine mögliche Implementierung der erforderlichen Bash-Funktion:

# Print the newest file, if any, matching the given pattern
# Example usage:
#   newest_matching_file 'b2*'
# WARNING: Files whose names begin with a dot will not be checked
function newest_matching_file
{
    # Use ${1-} instead of $1 in case 'nounset' is set
    local -r glob_pattern=${1-}

    if (( $# != 1 )) ; then
        echo 'usage: newest_matching_file GLOB_PATTERN' >&2
        return 1
    fi

    # To avoid printing garbage if no files match the pattern, set
    # 'nullglob' if necessary
    local -i need_to_unset_nullglob=0
    if [[ ":$BASHOPTS:" != *:nullglob:* ]] ; then
        shopt -s nullglob
        need_to_unset_nullglob=1
    fi

    newest_file=
    for file in $glob_pattern ; do
        [[ -z $newest_file || $file -nt $newest_file ]] \
            && newest_file=$file
    done

    # To avoid unexpected behaviour elsewhere, unset nullglob if it was
    # set by this function
    (( need_to_unset_nullglob )) && shopt -u nullglob

    # Use printf instead of echo in case the file name begins with '-'
    [[ -n $newest_file ]] && printf '%s\n' "$newest_file"

    return 0
}

Es verwendet nur Bash-Builtins und sollte Dateien behandeln, deren Namen Zeilenumbrüche oder andere ungewöhnliche Zeichen enthalten.

4
pjh

Ungewöhnliche Dateinamen (z. B. eine Datei, die das gültige \n-Zeichen enthält, kann bei dieser Art von Analyse Verwüstungen verursachen. Hier eine Möglichkeit, dies in Perl zu tun:

Perl -le '@sorted = map {$_->[0]} 
                    sort {$a->[1] <=> $b->[1]} 
                    map {[$_, -M $_]} 
                    @ARGV;
          print $sorted[0]
' b2*

Das ist eine Schwartzian-Transformation , die dort verwendet wird.

2
glenn jackman

Es gibt einen viel effizienteren Weg, dies zu erreichen. Betrachten Sie den folgenden Befehl:

find . -cmin 1 -name "b2*"

Mit diesem Befehl wird die letzte vor genau einer Minute mit der Wildcard-Suche auf "b2 *" erzeugte Datei gefunden. Wenn Sie Dateien von den letzten zwei Tagen erhalten möchten, können Sie den folgenden Befehl verwenden:

find . -mtime 2 -name "b2*"

Das "." repräsentiert das aktuelle Verzeichnis. Hoffe das hilft.

1
Naufal

Dunkle magische Funktionsbeschwörung für diejenigen, die die find ... xargs ... head ...-Lösung oben möchten, aber in einer einfach zu verwendenden Funktionsform, damit Sie nicht denken müssen:

#define the function
find_newest_file_matching_pattern_under_directory(){
    echo $(find $1 -name $2 -print0 | xargs -0 ls -1 -t | head -1)
}

#setup:
#mkdir /tmp/files_to_move
#cd /tmp/files_to_move
#touch file1.txt
#touch file2.txt

#invoke the function:
newest_file=$( find_newest_file_matching_pattern_under_directory /tmp/files_to_move/ bc* )
echo $newest_file

Drucke:

file2.txt

Welches ist:

Der Dateiname mit dem ältesten geänderten Zeitstempel der Datei unter dem angegebenen Verzeichnis stimmt mit dem angegebenen Muster überein.

0
Eric Leschinski

Sie können stat mit einem Datei-Glob und einem Dekorate-Sort-Undecorate mit der hinzugefügten Dateizeit verwenden:

$ stat -f "%m%t%N" b2* | sort -rn | head -1 | cut -f2-
0
dawg