Acho que a solução mais curta para atender a todos os critérios é esse
awk '{
if (match($0, "^(.*)_[^_]+$", a)) {
print substr(a[1], 1, 27) (length(a[1]) > 27 ? "..." : "")
}
}'
Eu tenho um script que lista vários arquivos que correspondem a um determinado critério. Ele produz apenas nomes de arquivos e há um monte de texto desnecessário.
Uma string de exemplo é:
[gg]_Magi_-_13_[DB38165F].mkv
O que eu gostaria de alcançar na saída é:
[gg]_Magi_-_13
Eu pude substituir os sublinhados, mas não tive sorte em cortar o [CRC32] .mkv com sucesso.
Além disso, limito o número de caracteres e coloco reticências no final, se eles ultrapassarem 28 caracteres, mas, mesmo que não ultrapasse 28 caracteres, ele ainda acrescenta as reticências no final.
O código para isso é:
print substr( $0, 0, 28 )"[…]"}
Ajuda sobre qualquer um desses problemas seria muito apreciada.
Acho que a solução mais curta para atender a todos os critérios é esse
awk '{
if (match($0, "^(.*)_[^_]+$", a)) {
print substr(a[1], 1, 27) (length(a[1]) > 27 ? "..." : "")
}
}'
sed -e 's/_\[.*\.mkv//' -e 's/^\(.\{28\}\).*/.../' file.txt
O primeiro bit retira o _[blah].mkv
, e o segundo bit imprime os primeiros 28 caracteres e coloca o ...
no final - mas se a string tiver menos de 28 caracteres, ele apenas imprime o nome do arquivo removido. sem adicionar as elipses.
Se a extensão do arquivo não for sempre * .mkv, você pode usar isso (em sed, $
significa 'até o final da linha'):
sed -e 's/_\[.*$//' -e 's/^\(.\{28\}\).*/.../' file.txt
Embora awk
, sed
e empresa tenham seus méritos, eles não são necessários para isso. Você pode facilmente obter tudo o que solicitou usando apenas bash
de operações de sequência e correspondência de padrões. Supondo que você tenha atribuído o nome do seu arquivo a $name
:
name="${name%_\[*\].*}"
cortará o tipo de arquivo e o CRC entre parênteses de $name
. Se você precisa ter 100% de certeza de que apenas corta os CRCs, você pode usar um regex estendido em vez do acima:
[[ $name =~ (.*)_\[[[:xdigit:]]{8}\]\..*$ ]] && name="${BASH_REMATCH[1]}"
O truncamento de nomes com mais de 28 caracteres é então obtido por:
(( ${#name} > 28 )) && name="${name::27}…"
- duas linhas de bash
total (sem contar a lógica, loop ou outro, para obter seus nomes de arquivos no var, e o código de saída, é claro), sem externos. A principal vantagem é que o código é muito rápido, já que o shell nunca precisa executar binários externos.
Tente esta função bash (aviso: não testado):
function convert_filename {
# Regex guide:
# ^(.*)_? everything since the beginning of the string,
# optionally followed by an underscore
# \[[a-fA-F0-9]{8}\] 8 hexadecimal characters, surrounded by []
# \.(.\w+)$ filename extension at the end of the string
local r="$(echo "$1" | sed -r 's/^(.*)_?\[[a-fA-F0-9]{8}\]\.(.\w+)$//')"
if (( ${#r} < 28 )); then
# Outputs $r
echo "$r"
else
# Outputs the first 27 characters from $r followed by an ellipsis
echo "${r::27}…"
fi
}
Não é a solução mais limpa, mas você pode fazer isso:
echo "[gg]_Magi_-_13_[DB38165F].mkv" | awk -F '_' '{print $1"_"$2"_"$3"_"$4}'
EDIT: Meh, risque esta resposta. Não vai te dar a elipse.