Indexar uma string no bash

14

Como posso me referir a uma string pelo índice em sh / bash? Ou seja, basicamente dividindo.

Estou tentando retirar 5 caracteres de um nome de arquivo. Todos os nomes têm a estrutura: name_nr_code. Estou tentando remover o bit de código 5 alfanumérico. name_nr_tem sempre 10 caracteres.

Existe algo como;

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

Pierre B
fonte
5
Por que a bashtag se você está pedindo uma shsolução?
Stéphane Chazelas

Respostas:

14

Simples assim.

(bater)

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

Voila.

E uma explicação do Advanced Bash-Scripting Guide ( Capítulo 10. Manipulando Variáveis ) , (com NOTEs adicionais embutidos para destacar os erros nesse manual):

Extração de Substring

${string:position}

Extrai substring de $stringem $position.

Se o $stringparâmetro for "*" ou "@", extrai os parâmetros posicionais, iniciando em $position.

${string:position:length}

Extrai $lengthcaracteres de substring de $stringàs $position.

NOTEcotações ausentes em torno de expansões de parâmetros! echonão deve ser usado para dados arbitrários.

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.

Os argumentos de posição e comprimento podem ser "parametrizados", isto é, representados como uma variável, e não como uma constante numérica.


Se o $stringparâmetro for "*" ou "@", extrai o máximo de $lengthparâmetros posicionais, começando em $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 substré uma extensão GNU.

expr substr $string $position $length

Extrai $lengthcaracteres a $stringpartir de $position.

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

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

NOTE: Isso echoé redundante e o torna ainda menos confiável. Use expr substr + "$string1" 1 2.

NOTE: exprretornará com um status de saída diferente de zero se a saída for 0 (ou -0, 00 ...).


Entre. O livro está presente no repositório oficial do Ubuntu como abs-guide.

tomas
fonte
Dizer "posição" é um pouco enganador, pois na verdade é um deslocamento, o que significa que ${var:1}não retorna o valor da var"1ª posição", mas sim da 2ª posição.
Kusalananda
Isso é verdade, mas desde que você não concorde, pode haver uma posição zero. O que é bom para mim.
9

No POSIX sh,

  • "${var%?????}"é $varretirado dos últimos 5 caracteres à direita (ou $varse $varcontiver menos de 5 caracteres)

  • "${var%"${var#??????????}"}"são os 10 primeiros caracteres de $var.

  • "${var%_*}"é $varretirado da string mais curta que corresponde _*ao final de $var( foo_bar_baz-> foo_bar).
  • "${var%%_*}": correspondência igual, porém mais longa, em vez da correspondência mais curta ( foo_bar_baz-> foo).
  • se você deseja obter foo_bar_: "${var%"${var##*_}"}"( ${var##pattern}é o mesmo que ${var%%pattern}procurar o padrão no início e $varnão no final).

Com zsh:

  • $var[1,-6] do primeiro caractere para o sexto a partir do final (todos, exceto os últimos 5).
  • $var[1,10] para os 10 primeiros caracteres.

Com ksh, bashou zsh:

  • "${var:0:10}": primeiros 10 caracteres de $var

Com bashou zsh:

  • "${var:0:-5}": todos, exceto os últimos 5 caracteres (emite um erro e sai do script se $varestiver definido, mas contém menos de 5 caracteres, também quando $varnão estiver definido zsh).

Se você precisa de shcompatibilidade com Bourne , é muito difícil fazer isso com segurança. Se você pode garantir que o resultado não termine com caracteres de nova linha, é possível:

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\}'`

Você também terá um limite no comprimento de $var(variando entre sistemas).

Em todas essas soluções, se houver $varbytes que não possam formar parte de caracteres válidos, YMMV.

Stéphane Chazelas
fonte
meu, eles realmente criaram uma sintaxe feia para dentro desses aparelhos.
cat
2

shnão fornece uma maneira integrada de obter uma subcadeia de caracteres de uma string (tanto quanto eu posso ver), mas bashvocê pode fazer isso

${i:0:10}

Isso fornecerá os dez primeiros caracteres do valor da variável i.

O formato geral é ${variable:offset:length}.

Kusalananda
fonte
2

A maioria dos shells suporta algum tipo de expansão de parâmetro que pode ajudá-lo. No bash, você pode usar

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

Em dash, compensações não são suportadas, mas você pode usar padrões à esquerda e à direita:

remove_first3=${string#???}
remove_last2=${string%??}
choroba
fonte
0

Primeiro, não use um forloop para nomes de arquivos.

Então, algo assim deve ajudar.

find ./ -type f | while read filename ;do
  newfilename=$(echo ${filename}|cut -c 1-10)
  mv ${filename} ${newfilename}
done
MelBurslan
fonte
3
Por que é ruim usar forcom nomes de arquivos?
choroba
Cite suas variáveis ​​e use printfpara ser mais seguro. ... e read -r.
Kusalananda
3
O forloop do OP estava bom, exceto talvez pelos desaparecidos --. Eu posso ver pelo menos 10 bugs em suas 4 linhas de código! muitos dos quais bem conhecida má prática como assumindo os nomes de arquivos são uma única linha, o uso de eco, citações desaparecidas
Stéphane Chazelas