Dividindo string, extraindo chars e juntando novamente

4

Eu tenho strings na forma de wva/sia/e1 , bct/e2 , sv/de/e11 . É sempre <Part1>/e<NUM> ou <Part1>/<Part2>/e<NUM> . O que eu quero é encurtar as cordas, mantendo as primeiras letras das partes e descartando as barras ee:

wva/sia/e1 > ws1
bct/e2 > b2
sv/de/e11 > sd11

Como posso fazer isso dentro de um script sh?

Editar: a string representa um nome de trabalho:

[...]
job_name= # e.g. 'wva/sia/e1'
job_name=cut_name(job_name) # e.g. 'ws1'
[...]
    
por user1406177 07.04.2017 / 22:50

3 respostas

5

Na forma de um script como o que você pede:

#!/usr/bin/env python3
import sys

# read the input, split by /
st = sys.argv[1].split("/")
# get the first char of all sections *but* the last one
# add the last *from* the first character
print("".join([s[0] for s in st][:-1])+st[-1][1:])

Observe que isso funciona para qualquer duração, por exemplo:

wva/sia/bct/wva/sia/e1

se tornará

wsbws1

contanto que a última seção termine com /e<num>

Para usar

  1. Copie o script em um arquivo vazio, salve-o como rearrange.py
  2. Execute-o com a string como argumento, por exemplo:

    python3 /path/to/rearrange.py wva/sia/e1
    
    > ws1
    

Explicação

O script basicamente se explica, mas também é comentado.

    
por Jacob Vlijm 07.04.2017 / 23:06
3

Bash 4.3 de uma linha

Digamos que não precisamos de um roteiro completo. Bash tem recursos suficientes que nos permitem fugir com um one-liner. Aqui está um:

bash-4.3$ (read -r var ;IFS='/'; printf "%c" ${var%/*};echo ${var##*[^0-9]}) <<<  "sv/de/e11"
sd11

O que está acontecendo?

  • tudo acontece em subshell, portanto ( ) em todo o comando
  • usamos aqui a string <<< para enviar entrada, e o comando subshell obtém via read -r var e armazena em var variable
  • definimos IFS='/' para que o subshell divida var em itens separados no separador / . Isso é importante para a divisão de palavras.
  • em seguida, usamos a remoção do sufixo ${var%/*} para nos livrarmos da última parte antes de / . No exemplo acima, seria e11
  • printf "%c" verá o resultado de ${var%/*} como sv de devido à remoção de palavras e sufixos mencionados acima (mágica, sim). Por causa de como printf words, %c imprimirá apenas o primeiro caractere, mas fará isso para cada argumento de linha de comando que receber, assim, para sv de , será gerada s e d . A impressão é feita sem nova linha, portanto, parece que os caracteres são digitados em seqüência
  • echo ${var##*[^0-9]} faz uso da remoção de prefixo para se livrar de todos os caracteres não dígitos na string de entrada fornecida, obtendo assim apenas os últimos dígitos

Existe outra abordagem de uma linha, que é um pouco mais explícita e natural para programadores tipo C.

bash-4.3$ (read -r inp;IFS='/';arr=( $inp ); for ((i=0;i<$(( ${#arr[@]} -1 ));i++));do printf "%s" ${arr[$i]:0:1};done;printf "%s\n" ${inp##*[^0-9]}) <<<  "sv/de/e11"
sd11

O que é essa mágica? Aqui está uma explicação:

  • Tudo acontece em subshell, portanto, () em todo o comando.
  • Usamos aqui a string <<< para enviar o item desejado para o fluxo de stdin do comando, e o comando obtém o comando read -r inp e o armazena em inp variable
  • Em seguida, alteramos a variável IFS para dividir tudo em uma matriz.
  • iteramos todos os itens até o anterior ao último usando o estilo C para o loop for ((initial condition; test condition; post condition)) ; do ... done
  • o $(( ${#arr[@]} - 1 )) é a expansão aritmética, onde subtraímos 1 do comprimento da matriz ${#arr[@]}
  • o printf "%s" ${arr[$i]:0:1} nos permite usar a expansão de parâmetro, onde imprimimos apenas o primeiro caractere de cada item, e printf "%s" imprime sem nova linha, assim parece que estamos imprimindo cada letra na mesma linha.
  • finalmente, depois que o loop terminar, pegamos o texto de entrada original e nos livramos de tudo que não é dígito usando a remoção de prefixo ${#*[^0-9]}

Abordagem de script

Como a pergunta pede por um shell script, aqui está um em bash 4.3, que é quase a mesma abordagem acima, mas mais explícito:

#!/bin/bash
IFS='/'
items=(  )
counter=1
for i in ${items[@]}
do
    if [ $counter -eq ${#items[@]}  ];
    then
        # note the space before -1
        printf "%s\n" "${i##*[^0-9]}"
    else
        printf "%s" "${i:0:1}"
    fi
    counter=$(($counter + 1)) 
done

A maneira como isso funciona é assim:

  • dada uma string na linha de comando como argumento, definimos o separador de campo interno como / e permitimos que o bash execute a divisão de palavras para dividir a string em uma matriz chamada items
  • iteramos todos os itens da matriz ${items[@]} , enquanto controlamos o item em que estamos usando a variável do contador e sabemos o número de itens na matriz (a ${#items[@]} part).
  • O if-statement é o que nos permite escolher um caractere específico de cada item. Usando expansão de parâmetro, primeiro caractere via ${i:0:1} . Usando a remoção de prefixo mais longa ${variable##prefix} , removemos todos os caracteres não dígitos da última string em printf "%s\n" "${i##*[^0-9]}" .

Aqui está em ação:

$ ./shorten_string.sh "wva/sia/e1"                         
ws1
$ ./shorten_string.sh "bct/e2"                             
b2
$ ./shorten_string.sh  "sv/de/e11"                     
sd11
    
por Sergiy Kolodyazhnyy 08.04.2017 / 00:09
1

OK, não é um script, mas você pode colocá-lo em um script (isso também é muito deselegante, já que falhei em lidar com os dois formulários em um único comando)

$ sed -r 's:(.).*/(.).*/e([0-9]+)::;s:(.).*/e([0-9]+)::' file
ws1
b2
sd11

Notas

  • -r use ERE
  • s:old:new: replace old com new
  • .* qualquer número de caracteres
  • (.) salve um caractere nessa posição
  • ([0-9]+) salve pelo menos um dígito aqui
  • ; separa comandos, como no shell
  • de referência anterior a caracteres salvos com ()
por Zanna 08.04.2017 / 19:04