Verificar se o formato está correto é fácil. Mas não acho que você possa, no bash (com built-ins), verificar se a data é válida.
RedX 14/01
Respostas:
317
Você pode usar a construção de teste [[ ]], juntamente com o operador de correspondência de expressão regular =~, para verificar se uma sequência corresponde a um padrão de expressão regular .
Para o seu caso específico, você pode escrever:
[[ $date =~^[0-9]{8}$ ]]&& echo "yes"
Ou mais, um teste preciso:
[[ $date =~^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$ ]]&& echo "yes"# |^^^^^^^^ ^^^^^^ ^^^^^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^ |# | | ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ |# | | | | |# | | \ | |# | --year-- --month-- --day-- |# | either 01...09 either 01..09 end of line# start of line or 10,11,12 or 10..29# or 30, 31
Ou seja, você pode definir um regex no Bash que corresponda ao formato desejado. Desta forma, você pode fazer:
[[ $date =~^regex$ ]]&& echo "matched"|| echo "did not match"
onde os comandos posteriores &&são executados se o teste for bem-sucedido e os comandos posteriores ||são executados se o teste for malsucedido.
Estou ciente disso, mas também gosto de levar em consideração quem está perguntando e até que ponto eles estão com o bash. Se fornecermos condições muito complexas, elas não aprenderão nada e só voltarão quando tiverem outra dúvida. Prefiro dar uma resposta mais compreensível.
fedorqui 'Então pare de prejudicar'
7
Heh. Bem, a única maneira de aprender é ler muito código bom. Se você der um código falso que é fácil de entender, mas não é recomendado o uso - é uma maneira ruim de ensinar. Também tenho certeza de que para aqueles que começaram a aprender bash (provavelmente já conhecem alguns bits de outro idioma) entenderão a sintaxe do bash para regex mais facilmente do que algum grepcomando com -Eflag.
Aleks-Daniel Jakimenko-A.
8
@ Aleks-DanielJakimenko Eu passei por este post novamente e agora concordo que é melhor usar o regex do bash. Obrigado por apontar na boa direção, resposta atualizada.
fedorqui 'SO stop prejudicar'
4
Upvote, que permite usá-lo um pouco além do que questão OP, por sh, por exemplo ..
Dereckson
3
@ Aleks-DanielJakimenko usando grep parece ser a melhor opção se você estiver usando sh, fishou outras conchas menos equipados.
tomekwi
47
Na versão 3 do bash, você pode usar o operador '= ~':
Avalie sua resposta com alto nível, pois permite que a função de data lide com as datas e não com as regexs propensas a erros ''
Ali
Isso é bom para verificar opções amplas de data, mas se você precisar verificar um formato de data específico, ele pode fazer isso? Por exemplo, se eu fizer, date -d 2017-11-14eele retornará Ter 14 de novembro 05:00:00 UTC 2017, mas isso quebraria meu script.
1937 Josias
1
Você pode usar algo assim: se ["2017-01-14" == $ (date -d "2017-01-14" '+% Y-% m-% d')] Testa se a data está correta e verifique se o resultado é o mesmo que os dados inseridos. A propósito, tenha muito cuidado com o formato de data localizado (mês-dia-ano vs. dia-mês-ano, por exemplo) #
1155 Django Janny
1
Pode não funcionar, dependendo da sua localidade. Datas americanas formatado usando MM-DD-AAAA não irá funcionar em qualquer outro lugar no mundo, usando DD-MM-AAAA (Europa) ou AAAA-MM-DD (alguns lugares na Ásia)
Paul
@Paul, o que pode não funcionar? Como escrito em um comentário, pode-se usar as opções de formatação ...
Betlista
4
Eu usaria em expr matchvez de =~:
expr match "$date""[0-9]\{8\}">/dev/null && echo yes
Isso é melhor do que a resposta de uso atualmente aceita, =~porque =~também corresponderá a cadeias vazias, o que IMHO não deveria. Suponha que badvarnão esteja definido, depois [[ "1234" =~ "$badvar" ]]; echo $?dê (incorretamente) 0, enquanto expr match "1234" "$badvar" >/dev/null ; echo $?dá o resultado correto 1.
Temos que usar >/dev/nullpara ocultar expr matcho valor de saída , que é o número de caracteres correspondidos ou 0 se nenhuma correspondência for encontrada. Observe que seu valor de saída é diferente de seu status de saída . O status de saída é 0 se houver uma correspondência encontrada ou 1 caso contrário.
Geralmente, a sintaxe para expré:
expr match "$string""$lead"
Ou:
expr "$string":"$lead"
onde $leadé uma expressão regular. Sua exit statusserá verdadeiro (0) se leadpartidas a principal fatia de string(há um nome para isso?). Por exemplo , expr match "abcdefghi" "abc"sai true, mas expr match "abcdefghi" "bcd"sai false. (Agradecemos a @Carlo Wood por apontar isso.
=~não está combinando cadeias vazias, você está combinando uma cadeia com um padrão vazio no exemplo que você fornece. A sintaxe é string =~ patterne um padrão vazio corresponde a tudo.
precisa saber é o seguinte
2
Isto não corresponde a uma substring, ele retorna (para stdout) o número de principais personagens que combinava e o estado de saída é verdadeira sse pelo menos 1 personagem foi correspondido. É por isso que uma string vazia (que corresponde a 0 caracteres) tem um status de saída false. Por exemplo expr match "abcdefghi" "^" && echo Matched || echo No match- e expr match "abcdefghi" "bcd" && echo Matched || echo No match- ambos retornam "0\nNo match". Onde a correspondência "a.*f"retornará "6\nMatched". O uso do '^' no seu exemplo é, portanto, também desnecessário e já está implícito.
Carlo Wood
@bstpierre: o ponto aqui não é se alguém pode racionalizar o comportamento de =~combinar cadeias vazias. É que esse comportamento pode ser inesperado e pode causar erros. Escrevi esta resposta especificamente porque fui queimada por ela.
Penghe Geng 30/06/19
@PengheGeng Comportamento inesperado? Se um padrão não possui definição ou restrições, na verdade corresponde a qualquer coisa. A ausência de um padrão é compatível com tudo. Escrever código robusto é a resposta, não justificando uma explicação ruim.
Anthony Rutledge
O "código robusto" do @AnthonyRutledge exige o melhor uso das ferramentas disponíveis para evitar erros acidentais de codificação. No código Shell, em que uma variável vazia pode ser introduzida de maneira fácil e acidental a qualquer momento por meio de erros de ortografia, não acho que permitir a correspondência de variáveis vazias seja um recurso robusto. Aparentemente, o autor do GNU exprconcorda comigo.
Penghe Geng 9/07/19
0
Onde o uso de uma regex pode ser útil para determinar se a sequência de caracteres de uma data está correta, ela não pode ser usada facilmente para determinar se a data é válida. Os exemplos a seguir passarão a expressão regular, mas são todas datas inválidas: 20180231, 20190229, 20190431
Portanto, se você deseja validar se sua sequência de datas (vamos chamá-la datestr) está no formato correto, é melhor analisá-la datee pedir datepara converter a sequência no formato correto. Se as duas strings forem idênticas, você terá um formato e uma data válidos.
Respostas:
Você pode usar a construção de teste
[[ ]]
, juntamente com o operador de correspondência de expressão regular=~
, para verificar se uma sequência corresponde a um padrão de expressão regular .Para o seu caso específico, você pode escrever:
Ou mais, um teste preciso:
Ou seja, você pode definir um regex no Bash que corresponda ao formato desejado. Desta forma, você pode fazer:
onde os comandos posteriores
&&
são executados se o teste for bem-sucedido e os comandos posteriores||
são executados se o teste for malsucedido.Observe que isso se baseia na solução de Aleks-Daniel Jakimenko na verificação de formato de data de entrada do usuário no bash .
Em outros shells, você pode usar o grep . Se o seu shell for compatível com POSIX, faça
No peixe , que não é compatível com POSIX, você pode fazer
fonte
grep
comando com-E
flag.sh
,fish
ou outras conchas menos equipados.Na versão 3 do bash, você pode usar o operador '= ~':
Referência: http://tldp.org/LDP/abs/html/bashver3.html#REGEXMATCHREF
fonte
Uma boa maneira de testar se uma string é uma data correta é usar a data do comando:
do comentário: pode-se usar a formatação
fonte
date -d 2017-11-14e
ele retornará Ter 14 de novembro 05:00:00 UTC 2017, mas isso quebraria meu script.Eu usaria em
expr match
vez de=~
:Isso é melhor do que a resposta de uso atualmente aceita,
=~
porque=~
também corresponderá a cadeias vazias, o que IMHO não deveria. Suponha quebadvar
não esteja definido, depois[[ "1234" =~ "$badvar" ]]; echo $?
dê (incorretamente)0
, enquantoexpr match "1234" "$badvar" >/dev/null ; echo $?
dá o resultado correto1
.Temos que usar
>/dev/null
para ocultarexpr match
o valor de saída , que é o número de caracteres correspondidos ou 0 se nenhuma correspondência for encontrada. Observe que seu valor de saída é diferente de seu status de saída . O status de saída é 0 se houver uma correspondência encontrada ou 1 caso contrário.Geralmente, a sintaxe para
expr
é:Ou:
onde
$lead
é uma expressão regular. Suaexit status
será verdadeiro (0) selead
partidas a principal fatia destring
(há um nome para isso?). Por exemplo ,expr match "abcdefghi" "abc"
saitrue
, masexpr match "abcdefghi" "bcd"
saifalse
. (Agradecemos a @Carlo Wood por apontar isso.fonte
=~
não está combinando cadeias vazias, você está combinando uma cadeia com um padrão vazio no exemplo que você fornece. A sintaxe éstring =~ pattern
e um padrão vazio corresponde a tudo.expr match "abcdefghi" "^" && echo Matched || echo No match
- eexpr match "abcdefghi" "bcd" && echo Matched || echo No match
- ambos retornam"0\nNo match"
. Onde a correspondência"a.*f"
retornará"6\nMatched"
. O uso do '^' no seu exemplo é, portanto, também desnecessário e já está implícito.=~
combinar cadeias vazias. É que esse comportamento pode ser inesperado e pode causar erros. Escrevi esta resposta especificamente porque fui queimada por ela.expr
concorda comigo.Onde o uso de uma regex pode ser útil para determinar se a sequência de caracteres de uma data está correta, ela não pode ser usada facilmente para determinar se a data é válida. Os exemplos a seguir passarão a expressão regular, mas são todas datas inválidas: 20180231, 20190229, 20190431
Portanto, se você deseja validar se sua sequência de datas (vamos chamá-la
datestr
) está no formato correto, é melhor analisá-ladate
e pedirdate
para converter a sequência no formato correto. Se as duas strings forem idênticas, você terá um formato e uma data válidos.fonte