Indeksuj ciąg w bash

15

Jak mogę odwoływać się do ciągu według indeksu w sh / bash? To znaczy w zasadzie dzielenie.

Próbuję usunąć 5 znaków z nazwy pliku. Wszystkie nazwy mają strukturę: nazwa_kod_nr. Usiłuję usunąć 5-bitowy kod kodu. name_nr_ma zawsze 10 znaków.

Czy istnieje coś takiego;

for i in * ; do mv "$i" "$i"[:10] ; done

Pierre B.
źródło
5
Dlaczego bashtag, jeśli pytasz o shrozwiązanie?
Stéphane Chazelas,

Odpowiedzi:

16

Proste jak to.

(grzmotnąć)

for i in * ; do mv -- "$i" "${i:0:5}" ; done

Voila

I wyjaśnienie z Advanced Bash-Scripting Guide ( rozdział 10. Manipulowanie zmiennymi ) , (z dodatkowymi NOTEwstawkami w celu podkreślenia błędów w tym podręczniku):

Ekstrakcja podciągów

${string:position}

Ekstrakty z podciąg $stringw $position.

Jeśli $stringparametr to „*” lub „@”, wówczas wyodrębnia parametry pozycyjne, zaczynając od $position.

${string:position:length}

Ekstrakty $lengthznaków podciąg od $stringw $position.

NOTEbrakujące cudzysłowy wokół rozszerzeń parametrów! echonie powinien być wykorzystywany do dowolnych danych.

stringZ=abcABC123ABCabc
#       0123456789.....
#       0-based indexing.

echo ${stringZ:0}                       # abcABC123ABCabc
echo ${stringZ:1}                       # bcABC123ABCabc
echo ${stringZ:7}                       # 23ABCabc 

echo ${stringZ:7:3}                     # 23A
                                        # Three characters of substring.


# Is it possible to index from the right end of the string?

echo ${stringZ:-4}                      # abcABC123ABCabc
# Defaults to full string, as in ${parameter:-default}.
# However . . . 

echo ${stringZ:(-4)}                    # Cabc
echo ${stringZ: -4}                     # Cabc
# Now, it works.
# Parentheses or added space "escape" the position parameter.

Argumenty pozycji i długości można „sparametryzować”, czyli przedstawić jako zmienną, a nie jako stałą liczbową.


Jeśli $stringparametrem jest „*” lub „@”, to wyodrębnia maksimum $lengthparametrów pozycyjnych, zaczynając od $position.

echo ${*:2}          # Echoes second and following positional parameters.
echo ${@:2}          # Same as above.

echo ${*:2:3}        # Echoes three positional parameters, starting at second.

NOTE: expr substrjest rozszerzeniem GNU.

expr substr $string $position $length

Wyodrębnia $lengthznaki od $stringpoczątku $position.

stringZ=abcABC123ABCabc
#       123456789......
#       1-based indexing.

echo `expr substr $stringZ 1 2`           # ab
echo `expr substr $stringZ 4 3`           # ABC

NOTE: Jest echoto zbędne i czyni go jeszcze mniej niezawodnym. Zastosowanie expr substr + "$string1" 1 2.

NOTE: exprpowróci z niezerowym statusem wyjścia, jeśli wyjście ma wartość 0 (lub -0, 00 ...).


BTW. Książka jest obecna w oficjalnym repozytorium Ubuntu jako abs-guide.

Tomas
źródło
Powiedzenie „pozycja” jest nieco mylące, ponieważ w rzeczywistości jest przesunięciem, co oznacza, że ${var:1}nie zwraca wartości varz „1. ​​pozycji”, ale w rzeczywistości z 2. pozycji.
Kusalananda
To prawda, ale dopóki się nie zgadzasz, pozycja zerowa może być zerowa. Co jest ze mną w porządku.
9

W POSIX sh,

  • "${var%?????}"jest $varpozbawiony ostatnich 5 znaków końcowych (lub $varjeśli $varzawiera mniej niż 5 znaków)

  • "${var%"${var#??????????}"}"to pierwsze 10 znaków $var.

  • "${var%_*}"jest $varusuwany z najkrótszego ciągu pasującego _*na końcu $var( foo_bar_baz-> foo_bar).
  • "${var%%_*}": takie same, ale najdłuższe dopasowanie zamiast najkrótszego dopasowania ( foo_bar_baz-> foo).
  • jeśli chcesz uzyskać foo_bar_: "${var%"${var##*_}"}"( ${var##pattern}jest taki sam jak ${var%%pattern}szukanie wzoru na początku $varzamiast na końcu).

Z zsh:

  • $var[1,-6] za pierwszą postać na 6. miejscu od końca (czyli wszystkie oprócz ostatnich 5).
  • $var[1,10] dla pierwszych 10 znaków.

Z ksh, bashlub zsh:

  • "${var:0:10}": pierwsze 10 znaków $var

Z bashlub zsh:

  • "${var:0:-5}": wszystkie oprócz ostatnich 5 znaków (daje błąd i wychodzi ze skryptu, jeśli $varjest ustawiony, ale zawiera mniej niż 5 znaków, również gdy $varnie jest ustawiony za pomocą zsh).

Jeśli potrzebujesz shzgodności z Bourne , bardzo trudno to zrobić niezawodnie. Jeśli możesz zagwarantować, że wynik nie skończy się na znakach nowej linii, możesz:

first_10=`expr " $var" : ' \(.{1,10\}\)'` # beware the exit status
                                          # may be non-zero if the
                                          # result is 0 or 0000000000

all_but_last_5=`expr " $var" : ' \(.*\).\{5\}'`

Będziesz także mieć limit długości $var(w zależności od systemu).

We wszystkich tych rozwiązaniach, jeśli $varzawiera bajty, które nie mogą stanowić części prawidłowych znaków, YMMV.

Stéphane Chazelas
źródło
mój, naprawdę wymyślili jakąś brzydką składnię dla tych nawiasów klamrowych.
kot
2

shnie zapewnia wbudowanego sposobu wyciągania podciągu z łańcucha (o ile widzę), ale z bashtobą możesz zrobić

${i:0:10}

To da ci pierwsze dziesięć znaków wartości zmiennej i.

Ogólny format to ${variable:offset:length}.

Kusalananda
źródło
2

Większość powłok obsługuje pewne rozszerzenia parametrów, które mogą ci pomóc. W bash możesz użyć

substr=${string:4:5} # start at position 4, length 5.

W dash, przesunięcia nie są obsługiwane, ale można użyć wzorców wiodących i końcowych:

remove_first3=${string#???}
remove_last2=${string%??}
choroba
źródło
0

Po pierwsze, nie używaj forpętli do nazw plików.

Zatem coś takiego powinno pomóc.

find ./ -type f | while read filename ;do
  newfilename=$(echo ${filename}|cut -c 1-10)
  mv ${filename} ${newfilename}
done
MelBurslan
źródło
3
Dlaczego używanie fornazw plików jest złe ?
choroba
Podaj swoje zmienne i użyj, printfaby być bezpieczniejszym. ... i read -r.
Kusalananda
3
forPętla OP była w porządku, z wyjątkiem może brakujących --. Widzę co najmniej 10 błędów w 4 liniach kodu! z których wiele znanych złych praktyk, takich jak zakładanie, że nazwy plików są jednowierszowe, używają echa, brakujących cytatów
Stéphane Chazelas