@MestreLion Wiele razy ludzie czytają pytanie, aby znaleźć rozwiązanie problemu. Ten zaczyna się od fałszywej przesłanki, która cutobsługuje coś, czego nie ma. Ale pomyślałem, że to przydatne, ponieważ zmusza czytelnika do rozważenia kodu, który jest łatwiejszy do naśladowania. Chciałem szybki, prosty sposób użycia cut, bez konieczności korzystania z wielu składnie dla awk, grep, seditp revrzeczą wystarczyły; bardzo elegancki i coś, czego nigdy nie rozważałem (nawet jeśli jest niezgrabny w innych sytuacjach). Lubiłem też czytać inne podejścia z innych odpowiedzi.
Beejor
3
Przyszedł tutaj prawdziwy problem: chcę znaleźć wszystkie różne rozszerzenia plików w drzewie źródłowym, aby zaktualizować plik .gitattributes. Taka find | cut -d. -f<last>jest naturalna skłonność
studog
Odpowiedzi:
679
Możesz spróbować czegoś takiego:
echo 'maps.google.com'| rev | cut -d'.'-f 1| rev
Wyjaśnienie
rev zmienia się na „maps.google.com” moc.elgoog.spam
cut używa kropki (tzn. „.”) jako separatora i wybiera pierwsze pole, którym jest moc
Nie używa tylko, cutale jest bez sedlub awk. Więc co sądzą OP?
Jayesh Bhoi
7
@tom OP zadał więcej pytań niż tylko to w ciągu ostatnich kilku godzin. Na podstawie naszych interakcji z OP wiemy, że awk / sed / etc. nie są dozwolone w swojej pracy domowej, ale nie ma odniesienia do rew. Warto było
spróbować
4
@zfus Rozumiem. Może revpotem przykleić kolejną .
tom
17
podwójny revświetny ideał!
Ford Guo
6
Niesamowite, proste, idealne, dziękuję również za wyjaśnienia - za mało ludzi tłumaczy każdy krok długim łańcuchem poleceń
Pete
128
Użyj rozszerzenia parametru. Jest to o wiele bardziej wydajne niż jakiekolwiek zewnętrzne polecenie cut(lub grep) włączone.
data=foo,bar,baz,qux
last=${data##*,}
Zobacz BashFAQ # 100, aby zapoznać się z wprowadzaniem natywnych operacji na łańcuchach w bash.
@ErwinWessels: Ponieważ bash jest naprawdę wolny. Użyj bash do uruchamiania potoków, a nie do masowego przetwarzania danych. Mam na myśli, że jest to świetne, jeśli masz jeden wiersz tekstu w zmiennej powłoki lub jeśli chcesz zrobić, while IFS= read -ra array_var; do :;done <(cmd)aby przetworzyć kilka wierszy. Ale w przypadku dużego pliku rev | cut | rev jest prawdopodobnie szybszy! (I oczywiście awk będzie szybszy.)
Peter Cordes,
2
@PeterCordes, awk będzie szybszy dla dużego pliku, jasne, ale potrzeba sporo wkładu, aby pokonać stałe koszty uruchomienia. (Istnieją również powłoki - jak ksh93 - o wydajności zbliżonej do awk, gdzie składnia podana w tej odpowiedzi pozostaje poprawna; bash jest wyjątkowo powolny, ale nie jest nawet bliski jedynej dostępnej opcji).
Charles Duffy,
1
Dzięki @PeterCordes; jak zwykle myślę, że każde narzędzie ma swoje zastosowania.
Erwin Wessels,
1
Jest to zdecydowanie najszybszy i najbardziej zwięzły sposób przycięcia pojedynczej zmiennej w bashskrypcie (zakładając, że już używasz bashskryptu). Nie musisz dzwonić na nic zewnętrznego.
Ken Sharp
1
@Balmipour ... jednak revjest specyficzne cokolwiek OS używasz że zapewnia to - to nie jest ujednolicone we wszystkich systemach UNIX. Zobacz listę rozdziałów w sekcji POSIX na temat poleceń i programów narzędziowych - jej tam nie ma. I tak naprawdę nie${var##prefix_pattern} jest specyficzny dla bash; jest w standardzie sh POSIX , patrz koniec rozdziału 2.6.2 (połączony), więc w przeciwieństwie do tego, zawsze jest dostępny w dowolnej zgodnej powłoce. rev
Charles Duffy,
89
Nie można tego zrobić za pomocą just cut. Oto sposób użycia grep:
Aby zrobić odwrotnie i znaleźć wszystko oprócz ostatniego pola, wykonaj:grep -o '^.*,'
Ariel
2
Było to szczególnie przydatne, ponieważ revw moim przypadku dodano problem wielobajtowych znaków Unicode.
Brice
3
Próbowałem to zrobić na MinGW, ale moja wersja grep nie obsługuje -o, więc użyłem, sed 's/^.*,//'który zastępuje wszystkie znaki aż do ostatniego przecinka pustym łańcuchem.
TamaMcGlinn
46
Bez awk? ... Ale z awk jest to takie proste:
echo 'maps.google.com'| awk -F.'{print $NF}'
AWK jest o wiele potężniejszym narzędziem, które można mieć w kieszeni. -F, jeśli dla separatora pól NF jest liczbą pól (oznacza również indeks ostatniego)
Jest to uniwersalne i za każdym razem działa dokładnie zgodnie z oczekiwaniami. W tym scenariuszu użycie cutdo osiągnięcia ostatecznego wyniku PO jest jak użycie łyżki do „pokrojenia” steków (zamierzona gra słów :)). awkjest nóż do steków.
Hickory420
3
Unikaj niepotrzebnego użycia tego, echoponieważ może to spowolnić działanie skryptu przy długich plikach awk -F. '{print $NF}' <<< 'maps.google.com'.
Korzystając z tego rozwiązania, liczba pól może być rzeczywiście nieznana i zmieniać się od czasu do czasu. Ponieważ jednak długość linii nie może przekraczać LINE_MAX znaków lub pól, w tym znaku nowej linii, dowolna liczba pól nigdy nie może być częścią jako rzeczywisty warunek tego rozwiązania.
Tak, bardzo głupie rozwiązanie, ale myślę, że jedyne, które spełnia kryteria.
To nie używa sedlub awkteż nie używacut albo, więc nie jestem pewien, czy to kwalifikuje się jako odpowiedź na pytanie, jak jego brzmienie.
Nie działa to dobrze, jeśli przetwarzane są ciągi wejściowe, które mogą zawierać ukośniki. Obejściem tej sytuacji byłoby zastąpienie ukośnika przedniego innym znakiem, o którym wiesz, że nie jest częścią prawidłowego ciągu wejściowego. Na przykład |znak pipe ( ) nie jest także dozwolony w nazwach plików, więc działałoby to:
Dodając podejście do tego starego pytania tylko dla zabawy:
$ cat input.file # file containing input that needs to be processed
a;b;c;d;e
1;2;3;4;5
no delimiter here
124;adsf;15454
foo;bar;is;null;info
$ cat tmp.sh # showing off the script to do the job#!/bin/bash
delim=';'while read -r line;dowhile[["$line"=~"$delim"]];do
line=$(cut -d"$delim"-f 2-<<<"$line")done
echo "$line"done< input.file
$ ./tmp.sh # output of above script/processed input file
e
5
no delimiter here
15454
info
Oprócz bash stosuje się tylko cięcie. No i echo, tak myślę.
Eee, dlaczego po prostu całkowicie nie wyciąć i użyć tylko bash ... x] while read -r line; do echo ${line/*;}; done <input.filedaje ten sam wynik.
Kaffe Myers
-1
Uświadomiłem sobie, że jeśli upewnimy się, że istnieje separator końcowy, to zadziała. W moim przypadku mam separatory przecinków i białych znaków. Na końcu dodaję spację;
cut
rozkazie :)? dlaczego nie jakieś inne polecenia Linuksa?sed
lubawk
:perl -pe 's/^.+\s+([^\s]+)$/$1/'
.cut
obsługuje coś, czego nie ma. Ale pomyślałem, że to przydatne, ponieważ zmusza czytelnika do rozważenia kodu, który jest łatwiejszy do naśladowania. Chciałem szybki, prosty sposób użyciacut
, bez konieczności korzystania z wielu składnie dlaawk
,grep
,sed
itprev
rzeczą wystarczyły; bardzo elegancki i coś, czego nigdy nie rozważałem (nawet jeśli jest niezgrabny w innych sytuacjach). Lubiłem też czytać inne podejścia z innych odpowiedzi.find | cut -d. -f<last>
jest naturalna skłonnośćOdpowiedzi:
Możesz spróbować czegoś takiego:
Wyjaśnienie
rev
zmienia się na „maps.google.com”moc.elgoog.spam
cut
używa kropki (tzn. „.”) jako separatora i wybiera pierwsze pole, którym jestmoc
com
źródło
cut
ale jest bezsed
lubawk
. Więc co sądzą OP?rev
potem przykleić kolejną .rev
świetny ideał!Użyj rozszerzenia parametru. Jest to o wiele bardziej wydajne niż jakiekolwiek zewnętrzne polecenie
cut
(lubgrep
) włączone.Zobacz BashFAQ # 100, aby zapoznać się z wprowadzaniem natywnych operacji na łańcuchach w bash.
źródło
while IFS= read -ra array_var; do :;done <(cmd)
aby przetworzyć kilka wierszy. Ale w przypadku dużego pliku rev | cut | rev jest prawdopodobnie szybszy! (I oczywiście awk będzie szybszy.)bash
skrypcie (zakładając, że już używaszbash
skryptu). Nie musisz dzwonić na nic zewnętrznego.rev
jest specyficzne cokolwiek OS używasz że zapewnia to - to nie jest ujednolicone we wszystkich systemach UNIX. Zobacz listę rozdziałów w sekcji POSIX na temat poleceń i programów narzędziowych - jej tam nie ma. I tak naprawdę nie${var##prefix_pattern}
jest specyficzny dla bash; jest w standardzie sh POSIX , patrz koniec rozdziału 2.6.2 (połączony), więc w przeciwieństwie do tego, zawsze jest dostępny w dowolnej zgodnej powłoce.rev
Nie można tego zrobić za pomocą just
cut
. Oto sposób użyciagrep
:Zamień przecinek na inne ograniczniki.
źródło
grep -o '^.*,'
rev
w moim przypadku dodano problem wielobajtowych znaków Unicode.sed 's/^.*,//'
który zastępuje wszystkie znaki aż do ostatniego przecinka pustym łańcuchem.Bez awk? ... Ale z awk jest to takie proste:
AWK jest o wiele potężniejszym narzędziem, które można mieć w kieszeni. -F, jeśli dla separatora pól NF jest liczbą pól (oznacza również indeks ostatniego)
źródło
cut
do osiągnięcia ostatecznego wyniku PO jest jak użycie łyżki do „pokrojenia” steków (zamierzona gra słów :)).awk
jest nóż do steków.echo
ponieważ może to spowolnić działanie skryptu przy długich plikachawk -F. '{print $NF}' <<< 'maps.google.com'
.Istnieje wiele sposobów. Ty też możesz tego użyć.
Oczywiście, puste miejsce dla polecenia tr należy zastąpić potrzebnym ogranicznikiem.
źródło
Jest to jedyne możliwe rozwiązanie, w którym można użyć tylko cięcia:
Korzystając z tego rozwiązania, liczba pól może być rzeczywiście nieznana i zmieniać się od czasu do czasu. Ponieważ jednak długość linii nie może przekraczać LINE_MAX znaków lub pól, w tym znaku nowej linii, dowolna liczba pól nigdy nie może być częścią jako rzeczywisty warunek tego rozwiązania.
Tak, bardzo głupie rozwiązanie, ale myślę, że jedyne, które spełnia kryteria.
źródło
cut -f2-
w pętli, dopóki dane wyjściowe już się nie zmienią.Jeśli ciąg wejściowy nie zawiera ukośników, możesz użyć
basename
i podpowłoki:To nie używa
sed
lubawk
też nie używacut
albo, więc nie jestem pewien, czy to kwalifikuje się jako odpowiedź na pytanie, jak jego brzmienie.Nie działa to dobrze, jeśli przetwarzane są ciągi wejściowe, które mogą zawierać ukośniki. Obejściem tej sytuacji byłoby zastąpienie ukośnika przedniego innym znakiem, o którym wiesz, że nie jest częścią prawidłowego ciągu wejściowego. Na przykład
|
znak pipe ( ) nie jest także dozwolony w nazwach plików, więc działałoby to:źródło
następujące implementuje sugestię znajomego
źródło
echo
, aby działało to niezawodnie i solidnie. Zobacz stackoverflow.com/questions/10067266/…Jeśli masz plik o nazwie filelist.txt, który jest ścieżką do listy, taką jak: c: /dir1/dir2/file1.h c: /dir1/dir2/dir3/file2.h
możesz to zrobić: rev filelist.txt | cut -d "/" -f1 | obrót silnika
źródło
Dodając podejście do tego starego pytania tylko dla zabawy:
Oprócz bash stosuje się tylko cięcie. No i echo, tak myślę.
źródło
while read -r line; do echo ${line/*;}; done <input.file
daje ten sam wynik.Uświadomiłem sobie, że jeśli upewnimy się, że istnieje separator końcowy, to zadziała. W moim przypadku mam separatory przecinków i białych znaków. Na końcu dodaję spację;
źródło
ans="a, b, c"
produkujeb
, który nie spełnia wymagań „liczba pól jest nieznana lub zmienia się z każdą linią” .