Pytanie Zmień nazwę pliku wsadowego za pomocą Bash


W jaki sposób Bash może zmienić nazwę serii pakietów, aby usunąć ich numery wersji? Bawiłem się z tymi dwoma expr i %%, bez skutku.

Przykłady:

Xft2-2.1.13.pkg staje się Xft2.pkg

jasper-1.900.1.pkg staje się jasper.pkg

xorg-libXrandr-1.2.3.pkg staje się xorg-libXrandr.pkg


76
2018-03-02 15:20


pochodzenie


Zamierzam używać tego regularnie, jako skrypt jednorazowy do użycia w lotach. Każdy system, z którego będę korzystał, będzie miał bash na nim, więc nie boję się basha, które są bardzo przydatne. - Jeremy L
Bardziej ogólnie patrz również stackoverflow.com/questions/28725333/... - tripleee


Odpowiedzi:


Możesz użyć funkcji rozszerzania parametrów basha

for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done

Cytaty są potrzebne dla nazw plików ze spacjami.


149
2018-03-02 15:33



Podoba mi się to jak dotąd najlepsze! - Jeremy L
Ja też to lubię, ale używa funkcji specyficznych dla basha ... Zazwyczaj nie używam ich na rzecz "standardowego" sh. - Diego Sevilla
Do wyrzucania jednoliniowych interaktywnych przedmiotów zawsze używam tego zamiast sed-fu. Jeśli był to skrypt, tak, unikaj bashisms. - richq
Aby uniknąć ponownego uruchamiania błędów, możesz użyć tego samego wzorca w ".pkg "part, ie" for i in * - [0-9.].pkg; do mv $ i $ {i / - [0-9.] *. pkg / .pkg}; zrobione. "Ale błędy są niewinne (przechodzenie do tego samego pliku). - richq
Nie jest to rozwiązanie bash, ale jeśli masz zainstalowany perl, zapewnia on wygodne polecenie zmiany nazwy partii, po prostu wykonaj rename "s/-[0-9.]*//" *.pkg - Woofas


Jeśli wszystkie pliki znajdują się w tym samym katalogu, sekwencja

ls | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh

wykonam twoją pracę. The sed Polecenie utworzy sekwencję mv polecenia, które można następnie potokować w powłoce. Najlepiej najpierw uruchomić potok bez końca | sh w celu sprawdzenia, czy polecenie wykonuje to, co chcesz.

Aby powrócić do wielu katalogów, użyj czegoś podobnego

find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh

Zauważ, że w sed sekwencją grupowania wyrażenia regularnego są nawiasy poprzedzone ukośnikiem odwrotnym, \( i \)zamiast pojedynczych nawiasów ( i ).


27
2018-03-02 15:54



Przebaczam moją naiwność, ale czy nie uciekłbym z nawiasów z odwróconymi ukośnikami, żeby traktować ich jak literalne postacie? - devios1
W sed sekwencja wyrażeń regularnych jest (zamiast pojedynczego nawiasu). - Diomidis Spinellis
nie działa dla mnie, byłby zadowolony z twojej pomocy ... Sufiks pliku FROM jest dołączany do pliku TO: $ find. -typ d | sed -n 's /(.*)\/(.*) anysoftkeyboard (. *) / mv "\ 1 \ / \ 2anysoftkeyboard \ 3" "\ 1 \ / \ 2effectedkeyboard \ 3" / p' | sh >> >>>>> WYJŚCIE >>>> mv: zmień nazwę ./base/src/main/java/com/anysoftkeyboard/base/dictionaries na ./base/src/main/java/com/effectedkeyboard/base/dictionaries/dictionaries : Brak takiego pliku lub katalogu - Vitali Pom
Wygląda na to, że brakuje ci odwrotnego ukośnika w nawiasach. - Diomidis Spinellis


Zrobię coś takiego:

for file in *.pkg ; do
    mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done

przypuszczam, że wszystkie twoje pliki znajdują się w bieżącym katalogu. Jeśli nie, spróbuj użyć znaleźć, jak zaleca powyżej Javier.

EDYTOWAĆ: Również ta wersja nie używa żadnych funkcji specyficznych dla basha, jak inne powyżej, co prowadzi do większej przenośności.


9
2018-03-02 15:35



Wszystkie znajdują się w tym samym katalogu. Co myślisz o odpowiedzi rq? - Jeremy L
Nie lubię używać funkcji specyficznych dla basha, ale zamiast tego bardziej standardowego sh. - Diego Sevilla
Zakładam, że to była szybka zmiana nazwy, a nie pisanie nowej generacji platformy crossowej, przenośnej aplikacji korporacyjnej ;-) - richq
Haha, wystarczająco uczciwe ... Po prostu z takiego mężczyzny, który mnie lubi, jak ja :) - Diego Sevilla
Ten nie wyjaśnia trzeciego przykładu: xorg-libXrandr (łącznik używany w nazwie). - Jeremy L


lepsze wykorzystanie sed w tym celu coś takiego:

find . -type f -name "*.pkg" |
 sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
 while read nameA nameB; do
    mv $nameA $nameB;
 done

wyrażenie regularne jest pozostawione jako ćwiczenie (jak w przypadku nazw plików zawierających spacje)


3
2018-03-02 15:30



Dzięki: Spaces nie są używane w nazwach. - Jeremy L


Wydaje się, że to działa, zakładając, że

  • wszystko kończy się na $ pkg
  • Twoja wersja # zawsze zaczyna się od "-"

zdejmij .pkg, a następnie zdejmij - ..

for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done

1
2018-03-02 15:33



Istnieje kilka pakietów, które mają łącznik w nazwie (patrz trzeci przykład). Czy to wpływa na twój kod? - Jeremy L
Możesz używać wielu wyrażeń sed używając -e. Na przykład. sed -e 's/\.pkg//g' -e 's/-.*//g'. - nojak
Nie parsuj ls wydajność i podawaj swoje zmienne. Zobacz też shellcheck.net do diagnozowania typowych problemów i antepatterns w skryptach powłoki. - tripleee


Miałem wiele *.txt pliki, które mają zostać zmienione na .sql w tym samym folderze. poniżej pracował dla mnie:

for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done

1
2017-11-05 17:22



Możesz poprawić swoją odpowiedź, wyodrębniając ją do rzeczywistego przypadku użycia PO. - dakab
Ma to wiele problemów, jak również oczywisty błąd składni. Widzieć shellcheck.net na początek. - tripleee


Oto POSIX w pobliżu odpowiednik obecnie zaakceptowana odpowiedź. To handluje tylko z Bash ${variable/substring/replacement} Rozszerzenie parametru dla tego, które jest dostępne w dowolnej powłoce zgodnej z Bourne.

for i in ./*.pkg; do
    mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done

Rozszerzenie parametrów ${variable%pattern} produkuje wartość variable z dowolnym sufiksem, który pasuje pattern oddalony. (Jest również ${variable#pattern} aby usunąć prefiks.)

Zachowałem subpattern -[0-9.]* z zaakceptowanej odpowiedzi, choć być może wprowadza w błąd. To nie jest wyrażenie regularne, ale wzór globalny; więc nie oznacza "kreski, po których następuje zero lub więcej cyfr lub kropek". Zamiast tego oznacza "myślnik, po którym następuje liczba lub kropka, a następnie cokolwiek". "Cokolwiek" będzie najkrótszym możliwym meczem, a nie najdłuższym. (Oferty Basha ## i %% do przycinania najdłuższego możliwego prefiksu lub sufiksu, zamiast najkrótszego).


1
2018-03-04 19:41





Dziękuję za te odpowiedzi. Miałem też jakiś problem. Przenoszenie plików .nzb.queued do plików .nzb. W nazwach plików zawierał spacje i inne elementy, co rozwiązało mój problem:

find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh

Opiera się na odpowiedzi Diomidis Spinellis.

Regex tworzy jedną grupę dla całej nazwy pliku i jedną grupę dla części przed .nzb.queued, a następnie tworzy polecenie przeniesienia powłoki. Z cytowanymi ciągami znaków. Pozwala to również uniknąć tworzenia pętli w skrypcie powłoki, ponieważ jest to już wykonywane przez sed.


0
2017-10-30 08:53



Należy zauważyć, że dotyczy to tylko GNU sed. Na BSD sed nie będzie interpretować -n opcję w ten sam sposób. - ocodo


Możemy się domyślać sedjest dostępny na dowolnym * nix, ale nie możemy być tego pewni będzie wspierać sed -n do generowania poleceń mv. (UWAGA: Tylko GNU sed robi to.)

Mimo to, bash builtins i sed, możemy szybko uruchomić funkcję powłoki.

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      mv -v "$file" "$(sed $sed_pattern <<< $file)"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

Stosowanie

sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.pkg

before:

./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg

after:

./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg

Tworzenie folderów docelowych:

Od mv nie tworzy automatycznie folderów docelowych, których nie możemy użyć nasza początkowa wersja sedrename.

To dość mała zmiana, więc dobrze byłoby dodać tę funkcję:

Będziemy potrzebować funkcji użyteczności, abspath (lub ścieżka bezwzględna) od bash nie ma tej wersji.

abspath () { case "$1" in
               /*)printf "%s\n" "$1";;
               *)printf "%s\n" "$PWD/$1";;
             esac; }

Kiedy już to zrobimy, możemy wygenerować foldery docelowe dla a Sed / zmiana nazwy wzoru, który zawiera nową strukturę folderów.

Dzięki temu będziemy znać nazwy naszych folderów docelowych. Kiedy my zmień nazwę, musimy użyć jej na docelowej nazwie pliku.

# generate the rename target
target="$(sed $sed_pattern <<< $file)"

# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"

# finally move the file to the target name/folders
mv -v "$file" "$target"

Oto pełny skrypt świadomy folderu ...

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      target="$(sed $sed_pattern <<< $file)"
      mkdir -p "$(dirname $(abspath $target))"
      mv -v "$file" "$target"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

Oczywiście nadal działa, gdy nie mamy konkretnych folderów docelowych także.

Gdybyśmy chcieli umieścić wszystkie piosenki w folderze, ./Beethoven/ możemy to zrobić:

Stosowanie

sedrename 's|Beethoven - |Beethoven/|g' *.mp3

before:

./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3

after:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

Bonus zaokrąglony ...

Za pomocą tego skryptu można przenosić pliki z folderów do jednego folderu:

Zakładając, że chcemy zebrać wszystkie pliki dopasowane i umieścić je w bieżącym folderze możemy to zrobić:

sedrename 's|.*/||' **/*.mp3

before:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

after:

./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3

Uwaga dotycząca wzorów regexu sed

W tym skrypcie obowiązują regularne reguły sed, te wzorce nie są PCRE (Perforacja zgodna z Perl). Możesz mieć sed rozszerzona składnia wyrażenia regularnego, używając albo sed -r lub sed -E w zależności od twojej platformy.

Zobacz zgodność z POSIX man re_format dla pełnego opisu sed podstawowe i rozszerzone wzory regexp.


0
2018-03-11 01:50