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.