A maneira POSIX:
NL='
'
case $myvar in
*"$NL"*) echo more than one line ;;
*) echo one line ;;
esac
Isso também funciona em shells pré-POSIX semelhantes a Bourne, também.
Eu posso fazer isso chamando o utilitário externo sed
(para um não-vazio $myvar
) assim:
if [ "$(printf %s "$myvar" | sed -n '$=')" -eq 1 ]; then
echo "Your variable has only one line, proceeding"
else
echo "Error condition, variable must have only one line"
fi
Existe uma maneira de fazer isso usando apenas bash
builtins?
Melhor ainda, existe uma maneira de fazer isso de uma maneira especificada pelo POSIX, sem chamar utilitários externos?
(Esta questão é de curiosidade e encontrar melhores maneiras de fazer as coisas; o código acima faz funcionar. Eu estou querendo saber se existe uma maneira mais limpa / mais rápida.)
Os snippets a seguir funcionam em bash
(com e sem a opção -posix
):
#!/bin/bash
#!/bin/bash -posix
version_1 () { [[ "$myvar" = *$'\n'* ]]; }
version_2 () {
local newlines="${myvar//[^$'\n']/}"
[[ "${#newlines}" -eq 1 ]]
}
for test in version_1 version_2; do
if $test; then echo many lines; else echo one line; fi
done
Existem várias opções (bash primeiro, o POSIX está abaixo).
O código dentro de cada função pode ser facilmente usado fora.
#!/bin/bash
nl=$'\n'
aregex (){ [[ $a =~ $nl ]]; }
apattern (){ [[ $a == *$nl* ]]; }
acut (){ [[ $a != "${a%%"$nl"*}" ]]; }
areplace (){ [[ $a != "${a//"$nl"/}" ]]; }
acase (){ case $a in (*$nl*) true;; (*) false;; esac; }
aifs ()( IFS="$nl"
set -f; set -- x${a}x;
(( $# > 1 ));
)
aread (){ IFS="$nl" read -rd '' -a b <<<"x${a}x";
(( "${#b[@]}" > 1 )); }
Cada função é uma opção. Cada função sairá com um valor de retorno de 0 se houver apenas uma linha e um valor de retorno de 1 se a variável $a
tiver mais de uma nova linha (mais de um $'\n'
).
Depois que uma função é executada, isso imprimirá a linha necessária:
out=''; "$function" && out="more than "
printf "%9s = %sone line\n" "$1" "$out"
Executando todas as opções:
a='ab'"$nl"'cd' ; alltests
a='ab cd' ; alltests
Dá esta saída:
aregex = more than one line
apattern = more than one line
acut = more than one line
areplace = more than one line
acase = more than one line
aifs = more than one line
aread = more than one line
aregex = one line
apattern = one line
acut = one line
areplace = one line
acase = one line
aifs = one line
aread = one line
As seguintes opções falham no POSIX por vários motivos:
=~
no POSIX. ==
no POSIX. ${ / / }
. Quatro podem ser traduzidas com segurança para POSIX:
set -f
. -d
ou -a
, mas pode ser adaptado. #!/bin/dash
nl='
'
posixcut (){ [ "${a}" != "${a%%"$nl"*}" ] ; }
posixcase(){ case $a in (*$nl*) true;; (*) false;; esac; }
posixifs ()( IFS="$nl";
set -f; set -- x${a}x;
[ "$#" -gt 1 ];
)
posixread(){ local b=0;
while IFS=$nl read -r _; do
b=$((b+1));
[ $b -gt 1 ] && break;
done <<-_EOT_
x${a}x
_EOT_
[ $b -gt 1 ];
}
Nota : Sim, local
não é estritamente POSIX, mas é muito bem suportado .
O local para b
, pode ser removido com segurança (verifique o uso global de $b
).
Se você precisar de código para o shell Bourne 1977 original, use este:
posixcase() { case $a in *$nl*) true;; *) false;; esac; }
posixifs () ( IFS="$nl"
set -f; set -- x${a}x;
[ "$#" -gt 1 ];
)
bourneread()( b=0 ### A helper function.
while :; do
if IFS=$nl read c && [ $b -le 1 ]; then
b='expr $b + 1'
else
echo "$b"
break
fi
done <<-_EOT_
x${a}x
_EOT_
)
posixread (){ [ 'bourneread' -gt 1 ]; }
A expansão ${a%% }
usada no posixcut não funcionará em Bourne.
O posixifs precisa ser dividido em duas partes para funcionar em Bourne.
A opção de leitura precisa de mudanças de prefeito, mas funciona corretamente.
Sua função é posixread, o bourneread é uma função auxiliar necessária.
Todo o código do Bourne foi testado para funcionar corretamente com o shell da herança .
Aqui está uma maneira que deve funcionar com todas as shells da sintaxe Bourne / POSIX e usa somente builtins:
if (set -f ; IFS=$'\n'; set -- x${myvar}x ; [ $# = 1 ]) ; then
echo "Your variable has only one line, proceeding"
else
echo "Error condition, variable must have exactly one line"
fi
Se o seu shell não suporta IFS=$'\n'
(como dash
0.5.7), você pode usar:
IFS="
"
Eu recomendaria usar a expansão de parâmetros Bash da seguinte forma:
n="${myvar//[^\n]}"; if [ ${#n} -eq 1 ]; then
echo "Your variable has only one line, proceeding"
else
echo "Error condition, variable must have only one line"
fi
Amostras de teste:
myvar="xxx"
myvar="xx\nxx"
myvar="xx\nx\nx"
Tags bash posix shell-script string