Zmienianie nazw plików w celu dodania sufiksu

13

Potrzebuję polecenia, aby zmienić nazwę wszystkich plików w bieżącym katalogu roboczym, tak aby nowa nazwa pliku była taka sama jak stara, ale zawiera sufiks odpowiadający liczbie wierszy oryginalnych plików (np. Jeśli plik fma 10 wiersze, należy zmienić jego nazwę na f_10).

Oto moja (niedziałająca) próba:

 linenum=$(wc -l); find * -type f | grep -v sh | rename 's/^/ec/'*
Martin Yeboah
źródło
Możesz udzielić odpowiedzi! (Jeśli dodaje coś nowego do istniejących odpowiedzi - ale wygląda na to, że tak)
Volker Siegel

Odpowiedzi:

13

Co powiesz na:

for f in *; do mv "$f" "$f"_$(wc -l < "$f"); done

Na przykład:

$ wc -l *
 10 file1
 40 file2
100 file3
$ ls
file1_10  file2_40  file3_100

Jeśli chcesz zachować rozszerzenia (jeśli są obecne), użyj tego zamiast:

for f in *; do 
    ext=""; 
    [[ $f =~ \. ]] && ext="."${f#*.}; 
    mv "$f" "${f%%.*}"_$(wc -l < "$f")$ext; 
done
terdon
źródło
Dzięki, terdon! wydaje się, że ten wiersz poleceń kończy pracę, ale jak mogę zmienić kropkę (.) na (_)? Ponadto, ponieważ mój skrypt znajduje się w folderze, jego nazwa również się zmienia, a zatem pozwala na wykonanie skryptu raz (ponieważ nazwa skryptu się zmienia ...)
Martin Yeboah
@MartinYeboah zobacz zaktualizowaną odpowiedź na _. Co do skryptu, jaki skrypt? To powinno być uruchamiane z linii poleceń, nie ma potrzeby tworzenia skryptu. Jeśli chcesz, aby pasował tylko do niektórych plików, zmień na in *, na przykład, in *txtlub coś.
terdon
wielkie dzięki;) Całkowicie zapomniałem, że należy go wykonać z wiersza poleceń :)
Martin Yeboah
Myślę, że wc -l < $fzamiast grep byłoby łatwiej zrozumieć (i może lepiej wykonać, ale tego nie sprawdziłem).
musiKk
@musiKk tak, ale ponieważ używają innych odpowiedzi wc, chciałem pokazać inne podejście. Ale OK, w porządku.
terdon
10

Możesz wypróbować ten jeden liner:

find . -maxdepth 1 -type f -exec bash -c 'mv -i "$1" "$1.$(wc -l <"$1")"' _ {} \;
  • To znajdzie wszystkie pliki w bieżącym katalogu roboczym ( find . -maxdepth 1 -type f)

  • Następnie uruchamiamy instancję powłoki nad znalezionymi plikami, aby zmienić nazwy plików, aby dodać liczbę wierszy.

Przykład:

$ ls
bar.txt spam.txt foo.txt

$ find . -maxdepth 1 -type f -exec bash -c 'mv -i "$1" "$1.$(wc -l <"$1")"' _ {} \;

$ ls
bar.txt.12 foo.txt.24 spam.txt.7
heemayl
źródło
6

Innym sposobem, który zachowuje rozszerzenie (jeśli jest obecne) przy użyciu rename:

for f in *; do rename -n "s/([^.]+)(\.?.*)/\$1_$(< "$f" wc -l)\$2/" "$f"; done

Jeśli wynik jest oczekiwany, usuń -nopcję:

for f in *; do rename "s/([^.]+)(\.?.*)/\$1_$(< "$f" wc -l)\$2/" "$f"; done
kos
źródło
renameświetnie =) +1
AB
1
Czy to nie zawiedzie na abcd? Zmień drugą grupę przechwytywania na (\.?[^\.]+).
ps95,
@ prakharsingh95 To prawda, jednak (\.?[^\.]+)nie jest to w porządku, ponieważ będzie pasować tylko do drugiej kropki i nie będzie pasować do nazwy pliku z kolejnymi kropkami lub niezależnie od końcówki. To nie jest łatwe rozwiązanie trudne, jedynym sposobem wydaje się zrobić dwie zmiany.
Kos
@kos masz rację. Co powiesz na ([^ \.] +). *? ([^ \.] + $) Nie jestem pewien optymalności. Działające skrzypce: regex101.com/r/rQ9lX1/1
ps95
1
@ prakharsingh95 nie chcesz uciec .wewnątrz klas postaci.
terdon
5

Używanie find:

find . -maxdepth 1 -type f -print0 | while read -d $'\0' f; do mv "$f" "$f"_$(grep -c . "$f"); done

Przykład

% wc -l *
  3 doit
  5 foo

% find . -maxdepth 1 -type f -print0 | while read -d $'\0' f; do mv "$f" "$f"_$(grep -c . "$f"); done

% wc -l *                         
  3 doit_3
  5 foo_5
AB
źródło
Dlaczego -name "*"? Resztki z testów? ;)
Kos
1
@kos Stary nawyk: \
AB
3

Tylko dla zabawy i chichocze rozwiązanie rename. Ponieważ renamejest to narzędzie Perla, które akceptuje dowolny ciąg znaków, który jest ewaluowany, możesz wykonywać różnego rodzaju shenanigany. Rozwiązanie, które wydaje się działać, jest następujące:

rename 's/.*/open(my $f, "<", $_);my $c=()=<$f>;$_."_".$c/e' *
musiKk
źródło
Ciekawy pomysł +1
AB
2

Poniższy skrypt dotyczy wielu przypadków: pojedynczej kropki i rozszerzenia (file.txt), wielu kropek i rozszerzeń (file.1.txt), kolejnych kropek (file..foobar.txt) oraz kropek w nazwie pliku (pliku. Lub plik..).

Scenariusz

#!/bin/bash
# Author: Serg Kolo
# Date:  June 25,2015
# Description: script to rename files to file_numlines
# written for http://askubuntu.com/q/640430/295286

# Where are the files ?
WORKINGDIR=/home/xieerqi/substitutions
# Where do you want them to go ?
OUTPUTDIR=/home/xieerqi/substitutions/output

for file in $WORKINGDIR/* ;do 
    FLAG=0
    EXT=$(printf "%s" "$file" | awk -F'.' '{printf "%s",$NF }' )  # extension, last field of dot-separated string
    # EXT="${file##*.}" # Helio's advice is to use parameter expansion, but I dont know how to use it
    if [ -z $EXT ]; then # we have a dot at the end case file. or something
        # so we gotta change extension and filename
        EXT=""
        FILENAME=$(printf "%s" "$file" | awk -F '/' '{ print $NF}' )
        # set flag for deciding how to rename
        FLAG=1
    else
        FILENAME=$( printf "%s" "$file" | awk -F '/' -v var=$EXT '{gsub("."var,"");print $NF}'   ) # filename, without path, lst in
    fi

    NUMLINES=$(wc -l "$file" | awk '{print $1}') # line count

    if [ $FLAG -eq 0 ];then
         echo "$file" renamed as "$OUTPUTDIR"/"$FILENAME"_"$NUMLINES"."$EXT"
        # cp "$file" "$OUTPUTDIR"/"$FILENAME"_"$NUMLINES"."$EXT" # uncomment when necessary
    else
        echo "$file" renamed as "$OUTPUTDIR"/"$FILENAME"_"$NUMLINES""$EXT"
        # cp "$file" "$OUTPUTDIR"/"$FILENAME"_"$NUMLINES""$EXT" # uncomment when necessary
    fi

    #printf "\n"

done

Skrypt w akcji

$./renamer.sh                                                                                                           
/home/xieerqi/substitutions/file. renamed as /home/xieerqi/substitutions/output/file._0
/home/xieerqi/substitutions/file.. renamed as /home/xieerqi/substitutions/output/file.._0
/home/xieerqi/substitutions/file.1.jpg renamed as /home/xieerqi/substitutions/output/file.1_3.jpg
/home/xieerqi/substitutions/file.1.test.jpg renamed as /home/xieerqi/substitutions/output/file.1.test_3.jpg
/home/xieerqi/substitutions/file.1.test.txt renamed as /home/xieerqi/substitutions/output/file.1.test_2.txt
/home/xieerqi/substitutions/file.1.txt renamed as /home/xieerqi/substitutions/output/file.1_2.txt
/home/xieerqi/substitutions/file.2.jpg renamed as /home/xieerqi/substitutions/output/file.2_3.jpg
/home/xieerqi/substitutions/file.2.test.jpg renamed as /home/xieerqi/substitutions/output/file.2.test_3.jpg
/home/xieerqi/substitutions/file.2.test.txt renamed as /home/xieerqi/substitutions/output/file.2.test_2.txt
/home/xieerqi/substitutions/file.2.txt renamed as /home/xieerqi/substitutions/output/file.2_2.txt
/home/xieerqi/substitutions/foo..bar.txt renamed as /home/xieerqi/substitutions/output/foo..bar_4.txt

Zauważ, że w pliku nie ma linii. i plik .., stąd liczba wierszy wynosi 0

Specjalne podziękowania dla terdona i Helio za przejrzenie skryptu i sugerowane zmiany

Sergiy Kolodyazhnyy
źródło
2

Kolejny bash, opracowany z @Helio na czacie :

for file in *
do
    echo "$file"
    [[ -f "$file" ]] || continue
    [[ $file =~ (.*)(\.[^.]+)$ ]]
    cp "$file" "output/${BASH_REMATCH[1]:-$file}_$(wc -l < "$file")${BASH_REMATCH[2]}"
done

Dziwnie wyglądający monoklowany facet z karłowatą drugą głową ( (.*)(\.[^.]+)$) powinien pasować tylko do odpowiednich przedłużeń ( .foo, nie ..). Jeśli nie ma rozszerzenia, BASH_REMATCHtablica będzie pusta. Możemy to wykorzystać, używając domyślnej wartości nazwy pliku ${BASH_REMATCH[1]:-$file}i używając rozszerzenia w takiej postaci, w jakiej jest.

Do obsługi plików kropek można użyć find, jak sugerują terdon i Helio .

find -maxdepth 1 -type f -printf '%P\0' | 
while IFS= read -r -d '' file
do
    [[ $file =~ (.*)(\.[^.]+)$ ]]
    cp "$file" "output/${BASH_REMATCH[1]:-$file}_$(wc -l < "$file")${BASH_REMATCH[2]}"
done
muru
źródło
+1: Nie byłbym w stanie wyjaśnić polecenia. ;-)
Helio