Qual é a maneira mais simples de remover uma barra final de cada parâmetro?


91

Qual é a maneira mais simples de remover uma barra final de cada parâmetro no array '$ @', de modo que rsynccopie os diretórios por nome?

rsync -a --exclude='*~' "$@" "$dir"

O título foi alterado para esclarecimento. Para entender os comentários e responder sobre as várias barras finais, consulte o histórico de edição.


Respostas:


153

Você pode usar a ${parameter%word}expansão detalhada aqui . Aqui está um script de teste simples que demonstra o comportamento:

#!/bin/bash

# Call this as:
#   ./test.sh one/ two/ three/ 
#
# Output:
#  one two three

echo ${@%/}

34
+1: Ser altamente pedante, isso removerá uma única barra, nem todas as barras finais. Para remover qualquer número de barras finais:shopt -s extglob; echo "${@%%+(/)}"
glenn jackman

24
Aviso: você pode não querer remover as barras finais em todos os casos. Se "/" for fornecido como argumento, a remoção da barra final terá ... consequências desastrosas.
Gordon Davisson

13
PROTIP: Combine tr -s /com regex variável para remover barras repetidas e, em seguida, remova a barra final. por exemploDIR=$(echo //some///badly/written///dir////// | tr -s /); DIR=${DIR%/}
Dave

1
Realmente? Em um prompt bash, execute set -- one///// two// three four/; shopt -s extglob; echo "${@%%+(/)}"e me diga o que você vê
glenn jackman

1
@twalberg Agradeço "protips" que não são apenas respostas alternativas para a pergunta do OP em threads de comentários.
Kyle Strand

36

A resposta aceita cortará UMA barra final.

Uma maneira de aparar várias barras finais é assim:

VALUE=/looks/like/a/path///

TRIMMED=$(echo $VALUE | sed 's:/*$::')

echo $VALUE $TRIMMED

Quais saídas:

/looks/like/a/path/// /looks/like/a/path

1
Não se esqueça de citar suas variáveis, caso contenham espaços em branco: TRIMMED=$(echo "$VALUE" | sed 's:/*$::')
tetsujin

1
Na verdade, isso não é necessário dentro da $()construção. No entanto, também é inofensivo :) portanto, é provavelmente uma boa prática usar aspas duplas "$VALUE"para não ter que decidir quando e quando não usá-las.
Chris Johnson

Há alguma maneira de combinar isso com uma captura anterior? Eu quero remover o protocolo e (se houver) barra final de um URL, mas echo "https://www.example.com/foo/" | sed -e 's|https*://\(.*\)/*$|\1|'não funciona (uma vez que o grupo de captura corresponde à barra final, eu acho). Posso fazer isso com dois comandos: echo "https://www.example.com/foo/" | sed -e 's|https*://\(.*\)$|\1|' -e 's|/*$||'mas gostaria de saber se poderia ser feito com um?
Adam

Essa resposta funcionou melhor para mim com um arquivo de entrada muito grande. Um simples sed 's:/*$::' < in.txt > out.txtfaz o trabalho em segundos
MitchellK

19

Isso funciona para mim: ${VAR%%+(/)}

Conforme descrito aqui http://wiki.bash-hackers.org/syntax/pattern

Pode ser necessário definir a opção extglob do shell. Não consigo ver que está habilitado para mim, mas ainda funciona


2
Para consultar uma configuração: shopt extglobsem opções
glenn jackman

Este é o Extended Pattern Language e você deve definir extglob.
ingyhere

1
Isso não funciona no bash integrado do Mac OS X. A solução de Sean Bright acima faz:${VAR%/}
Alec Jacobson

12

realpathresolve determinado caminho. Entre outras coisas, ele também remove barras finais. Use -spara evitar seguir simlinks

DIR=/tmp/a///
echo $(realpath -s $DIR)
# output: /tmp/a

1
Requer que todos os nós no caminho, exceto o último, exista. Se o usuário lançar algum caminho de não existência, realpathfalhará.
Tito Lívio

2
@Livy realpath --canonicalize-missingfunciona absolutamente corretamente com qualquer parte não existente do caminho
maoizm

7

Para sua informação, adicionei essas duas funções à minha com .bash_profilebase nas respostas encontradas no SO. Como disse Chris Johnson, todas as respostas usando ${x%/}remove apenas uma barra, essas funções farão o que dizem, espero que seja útil.

rem_trailing_slash() {
    echo "$1" | sed 's/\/*$//g'
}

force_trailing_slash() {
    echo "$(rem_trailing_slash "$1")/"
}

4

No zsh, você pode usar o :amodificador.

export DIRECTORY='/some//path/name//'

echo "${DIRECTORY:a}"

=> /some/path/name

Isso funciona como, realpathmas não falha, com arquivos / diretórios ausentes como argumento.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.