Cortar todos os caracteres após o último /

14

Como cortar todos os caracteres após o último '/'?

Esse texto

xxxx/x/xx/xx/xxxx/x/yyyyy
xxx/xxxx/xxxxx/yyy

deve retornar

xxxx/x/xx/xx/xxxx/x
xxx/xxxx/xxxxx
Josef Klimuk
fonte
Boa pergunta, acabei de perceber que não entendíamos a pergunta da mesma maneira.
Félicien 27/02
3
Os comandos basenamee dirnamefaça isso já.
Michael Hampton
Não acho que a edição nº 5 da pergunta estivesse correta. Usar a palavra "recortar" pode ser ambíguo; se você corta alguma coisa, joga fora essa parte ou fica com apenas isso? No entanto, a redação anterior "e omitir o que está antes de /" era inequívoca. Isso foi alterado para o seu oposto.
Egmont
@egmont a palavra "cut" foi transferida do original: "Eu preciso cortar apenas os caracteres após o último /" se tornou "Como cortar todos os caracteres após o último '/'?" que é uma mudança de fraseado que mantém razoavelmente o significado, IMO. Na verdade, senti a parte "omitir o que é ..." ambígua: os exemplos parecem manter o que está antes do /. O exemplo, deixa claro, de qualquer maneira.
Muru
@ muru Estou falando da edição que adicionou os exemplos, não da sua edição.
Egmont

Respostas:

15

Se você deseja obter a "parte cortada"

yyy
yyyyy

Você pode usar

sed 's|.*/||' 

por exemplo.

echo "xxx/xxxx/xxxxx/yyy" | sed 's|.*/||'
echo "xxxx/x/xx/xx/xxxx/x/yyyyy" | sed 's|.*\/||'

resultado

yyy
yyyyy

(Nota: Isso usa a capacidade do sed de usar um separador diferente de /, neste caso |, no comando s)


Se você deseja obter o início da string:

xxx/xxxx/xxxxx
xxxx/x/xx/xx/xxxx/x

Você pode usar

sed 's|\(.*\)/.*|\1|'

por exemplo.

echo "xxx/xxxx/xxxxx/yyy" | sed 's|\(.*\)/.*|\1|'
echo "xxxx/x/xx/xx/xxxx/x/yyyyy" | sed 's|\(.*\)/.*|\1|'

resultado

xxx/xxxx/xxxxx
xxxx/x/xx/xx/xxxx/x
Félicien
fonte
6
Olá, neste caso, você pode alterar o delimitador, por exemplo, em #vez de /. Além disso, você pode usar a opção -rse você não quiser escapar `\` de cada caractere especial: sed -r 's#(^.*)/.*$#\1#'.
pa4080
1
Enviei uma edição proposta para usar | ao invés de /. Se você não gostar, sugiro que você "rejeite e edite" e mude "começando" para "começo". (Rejeite + edite, porque dessa forma não será aprovado pelos aprovadores de robôs. Como alternativa, basta reverter a alteração se ela tiver sido aprovada e você não gostar.)
Martin Bonner apoia Monica em
18

Se você está cortando fora as extremidades das cordas, dirnamepode caber a conta:

$ dirname xxxx/x/xx/xx/xxxx/x/yyyyy
xxxx/x/xx/xx/xxxx/x
$ _

Se você estiver tentando isolar a última parte da string, use echo /$(basename "$str").

$ str=xxxx/x/xx/xx/xxxx/x/yyyyy
$ echo /$(basename "$str")
/yyyyy
$ _

fonte
3
Honestamente, por que alguém sugeriria ou aceitaria uma resposta envolvendo sed (ou awk, ou qualquer outro método semelhante) em vez de apenas usar os utilitários instalados com o sistema operacional está além de mim.
Auspex
@Auspex Eu acho que é simplesmente porque dirnamee basenamenão são amplamente conhecidos, mas sede awksão.
Volker Siegel
@ Auspex: Como o dirname e o basename não operam no stdin, portanto cat text | basename, não funciona. Portanto, uma solução adequada para a pergunta do OP usando dirnamee basenameenvolveria xargsetc.
Pod
16

Expansão de parâmetros em bash

Você pode usar a expansão de parâmetros bash, neste caso

  • ${parameter%word}onde wordesta/*
  • ${parameter##word}onde wordesta*/

Exemplos:

Retire a última parte

$ asdf="xxx/xxxx/xxxxx/yyy"
$ echo ${asdf%/*}
xxx/xxxx/xxxxx

Isso é descrito em man bash:

${parameter%word}
${parameter%%word}
      Remove matching suffix pattern.  The word is expanded to produce
      a pattern just as in pathname expansion.  If the pattern matches
      a  trailing portion of the expanded value of parameter, then the
      result of the expansion is the expanded value of parameter  with
      the  shortest  matching  pattern (the ``%'' case) or the longest
      matching pattern (the ``%%'' case) deleted.  If parameter  is  @
      or  *,  the  pattern  removal operation is applied to each posi‐
      tional parameter in turn, and the  expansion  is  the  resultant
      list.   If  parameter is an array variable subscripted with @ or
      *, the pattern removal operation is applied to  each  member  of
      the array in turn, and the expansion is the resultant list.

Remova tudo, exceto a última parte

$ asdf="xxx/xxxx/xxxxx/yyy"
$ echo ${asdf##*/}
yyy

Você pode adicionar uma barra assim

$ echo /${asdf##*/}
/yyy

para obter exatamente o que você queria em uma instância específica, de acordo com a pergunta editada. Mas a pergunta foi editada por várias pessoas depois disso e não é fácil saber o que você deseja agora.

Isso é descrito em man bash:

${parameter#word}
${parameter##word}
      Remove matching prefix pattern.  The word is expanded to produce
      a pattern just as in pathname expansion.  If the pattern matches
      the  beginning of the value of parameter, then the result of the
      expansion is the expanded value of parameter with  the  shortest
      matching  pattern  (the ``#'' case) or the longest matching pat‐
      tern (the ``##'' case) deleted.  If parameter is  @  or  *,  the
      pattern  removal operation is applied to each positional parame‐
      ter in turn, and the expansion is the resultant list.  If param‐
      eter  is  an array variable subscripted with @ or *, the pattern
      removal operation is applied to each  member  of  the  array  in
      turn, and the expansion is the resultant list.
sudodus
fonte
10

Outra maneira é usar greppara exibir apenas a última barra por linha e o que segue:

$ grep -o '/[^/]*$' example.txt
/yyy
/yyyyy

Explicação:

-o diz grep para mostrar apenas a parte correspondente da linha em vez da linha inteira.

O padrão /[^/]*$corresponde a uma barra literal /seguida por qualquer caractere, exceto por uma barra [^/]qualquer número de vezes *até o final da linha $.

Byte Commander
fonte
8

Só porque outros publicaram respostas mais "sãs", eis uma pergunta um tanto boba:

rev | cut -d/ -f1 | rev

revinverte os caracteres em cada linha, por exemplo, abcd/ef/gtorna-se g/fe/dcba. Em seguida, cutcorta o primeiro segmento. Finalmente, é revertida novamente.

Egmont
fonte
2
Apesar da falta de sanidade desta resposta, especialmente por causa do encanamento da concha, eu gosto desta resposta :) Continue balançando, +1
Sergiy Kolodyazhnyy 28/18
Concordou - não são, mas foda!
Volker Siegel
3

Solução clássica com awk, que trata /como separador de campo para entrada e saída e define o último campo como string vazia (que é realmente "desreferenciando" a NFvariável número de campos ):

$ echo "xxx/xxxx/xxxxx/yyy"|awk  'BEGIN{OFS=FS="/"};{$NF="";print $0}'
xxx/xxxx/xxxxx/

Mais curto, como fedorqui apontou nos comentários:

$ echo "xxx/xxxx/xxxxx/yyy"|awk  'BEGIN{OFS=FS="/"};NF--'
xxx/xxxx/xxxxx/

Uma variação disso seria colocar o caminho no awkambiente de execução, o que economizará o encanamento, mas tornará a awkparte mais detalhada:

mypath="xxx/xxxx/xxxxx/yyy" awk  'BEGIN{OFS=FS="/"; sub(/\/([^\/]*)$/,"",ENVIRON["mypath"]);print(ENVIRON["mypath"]) };'
Sergiy Kolodyazhnyy
fonte
1
Muito detalhado, por que não apenas NF--? Desta forma, você avoud o printbla bla.
fedorqui 28/02
3

Adicionando à resposta do egmont porque a pergunta foi editada ...

Use --complement se desejar remover o primeiro campo com -f1 .

echo "xxxx/x/xx/xx/xxxx/x/yyyyy"|rev|cut -d/ -f1 --complement|rev
xxxx/x/xx/xx/xxxx/x

echo "xxxx/x/xx/xx/xxxx/x/yyyyy"|rev|cut -d/ -f1|rev
yyyyy

Além disso, a questão não é clara sobre o que deve acontecer com entradas que não contêm barras. xxxx => xxxx ou xxxx => nada

Ph3n0x
fonte
Você pode sugerir uma edição: askubuntu.com/posts/1010356/edit
muru
O @muru OP é um usuário de 1 representante. O privilégio de edição não é concedido em 2k? askubuntu.com/help/privileges/edit
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy qualquer um pode sugerir edições.
muru
+1 - Eu não havia notado a --complementopção antes e a página de manual é circular. Complemento significa complemento. Encontrei um bom tutorial de corte que o cobre em yourownlinux.com/2015/05// É como -vem grep. Me dê tudo, exceto o que foi combinado.
28418 Joe
2
-f2-é uma forma mais simples para -f1 --complement.
egmont