Używanie podstawiania zmiennych bash zamiast cut / awk

Czy mogę użyć podstawiania zmiennych bash do wyodrębnienia fragmentu zmiennej na podstawie separatora? Próbuję uzyskać bezpośrednią nazwę katalogu z nazwą pliku (w tym przypadku foo).

$ filename=./foo/bar/baz.xml 

Wiem, że mógłbym zrobić coś takiego

echo $filename | cut -d "/" -f 2 

lub

echo $filename | awk -F "/" "{print $2}" 

ale to ” powoli się rozwidla awk / cut dla wielu nazw plików.

Przeprowadziłem trochę profilowania różnych rozwiązań , używając moich prawdziwych plików:

echo | cut:

real 2m56.805s user 0m37.009s sys 1m26.067s 

echo | awk:

real 2m56.282s user 0m38.157s sys 1m31.016s 

@steeldriver „s podstawienie zmiennej / rozwinięcie parametrów powłoki:

real 0m0.660s user 0m0.421s sys 0m0.235s 

@jai_s „s IFS-wrangling:

real 1m26.243s user 0m13.751s sys 0m28.969s 

Obie sugestie były ogromnym ulepszeniem w stosunku do moich istniejących pomysłów, ale podstawianie zmiennych jest najszybsze, ponieważ nie wymagają tworzenia nowych procesów.

Komentarze

  • gnu.org/software/bash/manual / html_node / …
  • Wyślij wszystkie nazwy plików do jednego wywołania awk i będzie to znacznie szybsze niż jakiekolwiek rozwiązanie w czystym bashu
  • Czy nie możesz użyć tablicy, zrób je wszystkie naraz, a następnie umieść je w nowej tablicy?

Odpowiedź

Możesz usunąć najkrótszy początkowy podciąg, który pasuje do */

tmp="${filename#*/}" 

, a następnie usunąć najdłuższy końcowy podciąg pasujący do /*

Odpowiedź

 echo $f a/b/c $ (IFS="/";set $f; echo $1) a $ (IFS="/";set $f; echo $2) b $ (IFS="/";set $f; echo $3) c 

z symbolem wieloznacznym, z którym prawdopodobnie działa podwójne lub pojedyncze cudzysłowy –

 f="a?b?c" $(IFS="?"; set $f; echo $1) a echo $f a*b*c (IFS="*"; set $f; echo $1) a 

tak, „będziesz musiał cofnąć ustawienie IFS z powrotem do wartości domyślnych

 unset IFS 

Komentarze

  • Ups, podoba mi się to.
  • Zwykle jest to również moja preferowana metoda, ale pamiętaj, że Bash obsługuje tylko $1 do $9 przy użyciu tej składni. W przypadku dziesiątego i późniejszych argumentów należy użyć formularza ${10}.
  • Nie ' nie działa, gdy $f zawiera symbole wieloznaczne. Następnie musisz przywrócić IFS później (lub zrobić to w zamianie polecenia, aby uzyskać wartość pola, a to usuwa końcowe znaki nowej linii).
  • Przykład działa w izolacji (wewnątrz Git bash w systemie Windows), ale kiedy przesyłam potokiem z polecenia find, pojawia się ten błąd: echo: write error: Bad address.
  • OK, wygląda na to Za każdym razem muszę unset IFS.

Odpowiedź

Kanał listę do awk, aby przyspieszyć:

awk -F "/" "{print $2}" < <(find /usr) awk -F "/" "{print $2}" < inputfile 

Demonstracja:

time awk -F "/" "{print $2; SUM++} END {print "number of directories found: " SUM}" < <(find /usr -type d) usr usr . . number of directories found: 16748 real 0m8.910s user 0m0.050s sys 0m0.050s 

Odpowiedź

Dlaczego nie używasz polecenia„ dirname ”zamiast tego całego awk / sed / ciąć rzeczy?

filename=./foo/bar/baz.xml dirname $filename 

Plony:

./foo/bar 

Komentarze

  • W tym przypadku szukałem katalogu bezpośredniego, a nie pełnej ścieżki do katalogu.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *