2016-07-11 4 views
3

Ich habe ein Bash-Skript, das ein Verzeichnis als Parameter akzeptiert und nach einer gewissen Verarbeitung eine Ausgabe basierend auf den Dateien in diesem Verzeichnis ausführt.Entfernen eines optionalen/(Verzeichnisseparators) in Bash

Der Befehl wie die folgenden sein würde, wo dir ist ein Verzeichnis mit der folgenden Struktur innerhalb

dir/foo 
dir/bob 
dir/haha 
dir/bar 
dir/sub-dir 
dir/sub-dir/joe 

> myscript ~/files/stuff/dir 

Nach einiger Verarbeitung, würde ich die Ausgabe so etwas wie diese

foo 
bar 
sub-dir/joe 
zu sein

Der Code, den ich den übergebenen Pfad entfernen muss, ist folgender:

shopt -s extglob 

for file in $files ; do 
    filename=${file#${1}?(/)} 

Das bringt mich zu folgendem, aber aus irgendeinem Grund wird das optionale / nicht beachtet. So sieht meine Ausgabe wie folgt:

/foo 
/bar 
/sub-dir/joe 

Der Grund, warum ich es optional mache ist, weil, wenn der Benutzer führt den Befehl

> myscript ~/files/stuff/dir/ 

Ich will es noch arbeiten. Und wenn ich diesen Befehl mit dem Schrägstrich führe, wird er wie gewünscht ausgegeben.

Also, warum funktioniert meine ?(/) nicht? Basierend auf allem, was ich gelesen habe, sollte das die richtige Syntax sein, und ich habe auch ein paar andere Varianten ausprobiert, alles ohne Erfolg.

Danke.

+2

'#' passt so kurz wie möglich, so dass der optionale Teil niemals enthalten ist. Du könntest '##' verwenden, um so lange wie möglich zu arbeiten. –

+0

@thatotherguy, das hat funktioniert! Ah! Habe in der Dokumentation nichts davon gesehen. Wenn du das als Antwort aufsetzen willst, werde ich es akzeptieren, wenn ich kann. – David

Antwort

2

Hier ist die Dokumentation von man bash unter "Parameter Expansion":

${parameter#word} 
    ${parameter##word} 
      Remove matching prefix pattern. The word is 
      expanded to produce a pattern just as in pathname 
      expansion. If the pattern matches the beginning of 
      the value of parameter, then the result of the 
      expansion is the expanded value of parameter with 
      the shortest matching pattern (the ``#'' case) or 
      the longest matching pattern (the ``##'' case) 
      deleted. 

Da # versucht, die kürzeste Spiel zu löschen, wird es gehören nie nachlauf optionale Teile.

können Sie nur ## statt:

filename=${file##${1}?(/)} 

Je nachdem, was Ihr Skript tut und wie es funktioniert, können Sie es auch nur umschreiben zu cd in das Verzeichnis immer mit Pfaden arbeiten relativ zu .

3

Ich schlug vor, dass Sie files in ein Array ändern, das eine mögliche Problemumgehung für nicht standardmäßige Dateinamen ist, die Leerzeichen enthalten können.

files=("dir/A/B" "dir/B" "dir/C") 
for filename in "${files[@]}" 
do 
    echo ${filename##dir/} #replace dir/ with your param. 
done 

Ausgabe

A/B 
B 
C 
4

that other guy's helpful answer lösen Ihr unmittelbares Problem, aber es gibt zwei Dinge, nichts wert:

  • das Auflisten von Dateinamen mit einer nicht mit Anführungszeichen versehenen Zeichenfolgenvariablen (for file in $files) ist nicht ratsam, da sjsam's helpful answer darauf hinweist: Es wird mit Dateinamen mit eingebetteten Leerzeichen und Dateinamen, die wie Globs aussehen, unterbrochen; Wie bereits erwähnt, ist die Speicherung von Dateinamen in einem Array die robuste Wahl.

  • gibt es nicht unbedingt notwendig, shopt -s extglob globale Shell-Option zu ändern: Parameter Erweiterungen können verschachtelte sein, so dass die folgenden ohne Shell-Optionen zu ändern funktionieren würde:

# Sample values: 
file='dir/sub-dir/joe' 
set -- 'dir/' # set $1; value 'dir' would have the same effect. 

filename=${file#${1%/}} # -> '/sub-dir/joe' 

Der innere Parameter Erweiterung, ${1%/}, entfernt eine nachfolgende (%) / von $1, falls vorhanden.