Quiero combinar varias condiciones en una instrucción if de shell y negar la combinación. Tengo el siguiente código de trabajo para una combinación simple de condiciones:
if [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ; then
# do stuff with the files
fi
Esto funciona bien Si quiero negarlo, puedo usar el siguiente código de trabajo:
if ! ( [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ) ; then
echo "Error: You done goofed."
exit 1
fi
# do stuff with the files
Esto también funciona como se esperaba. Sin embargo, se me ocurre que no sé qué hacen los paréntesis en realidad allí. Yo quiero a utilizarlos sólo para la agrupación, pero ¿es realmente el desove un subnivel? (¿Cómo puedo saberlo?) Si es así, ¿hay alguna forma de agrupar las condiciones sin generar una subshell?
bash
shell-script
Comodín
fuente
fuente
if ! [ -f file1 ] || ! [ -f file 2 ] || ! [ -f file3 ] ; then
pero me gustaría una respuesta más general.if [[ ! -f file1 ]] && [[ ! -f file2 ]]; then
if [ ! 1 -eq 2 ] && [ ! 2 -eq 3 ]; then echo yep; fi
y funciona. Siempre escribo pruebas con llaves dobles como una costumbre. Además, para garantizar que no lo seabash
, lo probé másif /bin/test ! 1 -eq 2 && /bin/test ! 2 -eq 3 ; then echo yep; fi
y también funciona de esa manera.[ ! -e file/. ] && [ -r file ]
directorios. negarlo como quieras. por supuesto, eso es lo que-d
hace.Respuestas:
Debe usar en
{ list;}
lugar de(list)
:Ambos son Comandos de Agrupación , pero
{ list;}
ejecutan comandos en el entorno actual del shell.Tenga en cuenta que, el
;
en{ list;}
que se necesita para delimitar la lista de}
palabra inversa, se pueden utilizar otros delimitador también.{
También se requiere el espacio (u otro delimitador) después .fuente
awk
más vecesbash
) es la necesidad de espacios en blanco después de la llave abierta. Usted menciona "también puede usar otro delimitador"; algun ejemplo?zsh
, puedes usar espacios en blanco&
también es un separador, puede usar{ echo 1;:&}
, también se puede usar varias líneas nuevas .they must be separated from list by whitespace or another shell metacharacter.
En bash son:metacharacter: | & ; ( ) < > space tab
. Para esta tarea específica, creo que cualquiera de& ; ) space tab
ellos funcionará. .... .... De zsh (como comentario)each sublist is terminated by
&',
&!', or a newline.
Puede utilizar completamente la
test
funcionalidad para lograr lo que desea. Desde la página del manual detest
:Entonces su condición podría verse así:
Para negar el uso de paréntesis escapados:
fuente
A negar de forma portátil un condicional complejo en shell, debe aplicar la ley de De Morgan y empujar la negación hasta el fondo dentro de las
[
llamadas ...... o debes usar
then :; else
...if ! command
no está disponible de forma portátil y tampoco lo está[[
.Si no necesita portabilidad total, no escriba un script de shell . En realidad, es más probable que encuentres
/usr/bin/perl
en un Unix seleccionado al azar de lo que eresbash
.fuente
!
es POSIX que hoy en día es lo suficientemente portátil. Incluso los sistemas que todavía se envían con el shell Bourne también tienen un POSIX sh en otro lugar del sistema de archivos que puede usar para interpretar su sintaxis estándar./bin/sh
. Los scripts de Autoconf hacen lo que usted sugiere, pero creo que todos sabemos lo poco que es un respaldo.otros han notado la agrupación de
{
comandos compuestos;}
, pero si está realizando pruebas idénticas en un conjunto, es posible que desee utilizar un tipo diferente:... como se demuestra en otra parte
{ :;}
, no hay dificultad involucrada con los comandos compuestos de anidación ...Tenga en cuenta que lo anterior (normalmente) prueba los archivos normales . Si solo está buscando archivos existentes y legibles que no sean directorios:
Si no te importa si son directorios o no:
... funciona para cualquier archivo legible y accesible , pero es probable que se bloquee por quince sin escritores.
fuente
Esta no es una respuesta completa a su pregunta principal, pero noté que menciona las pruebas compuestas (archivo legible) en un comentario; p.ej,
Puede consolidar esto un poco definiendo una función de shell; p.ej,
Agregue manejo de errores a (p. Ej.
[ $# = 1 ]
) Al gusto. La primeraif
declaración, arriba, ahora se puede condensar ay puede acortar esto aún más acortando el nombre de la función. Del mismo modo, podría definir
not_readable_file()
(onrf
para abreviar) e incluir la negación en la función.fuente
readable_files() { [ $# -eq 0 ] && return 1 ; for file in "$@" ; do [ -f "$file" ] && [ -r "$file" ] || return 1 ; done ; }
(Descargo de responsabilidad: probé este código y funciona, pero no fue de una sola línea. Agregué puntos y comas para publicar aquí pero no probé su ubicación.)if readable_files file1 file2 file3
que no sabría si la función estaba haciendo AND u OR , aunque (a) supongo que es bastante intuitivo, (b) si no está distribuyendo el script, es lo suficientemente bueno si lo comprende, y (c) como sugerí abreviar el nombre de la función en la oscuridad, no estoy en posición de hablar sobre legibilidad. … (Continúa)for file in "$@" ; do
confor file do
- por defectoin "$@"
, y (algo contra-intuitivamente);
no solo es innecesaria, sino que realmente se desaconseja.También puede negar dentro de las pruebas de llaves, para reutilizar su código original:
fuente
||
lugar de&&
||
ejecutaría el predicado si alguno de los archivos no está presente, no si y solo si todos los archivos no están presentes. NO (A Y B Y C) es lo mismo que (NO A) Y (NO B) Y (NO C); Ver la pregunta original.||
lugar de hacerlo&&
. Vea mi primer comentario debajo de mi pregunta.not (a and b)
es lo mismo que(not a) or (not b)
. Ver en.wikipedia.org/wiki/De_Morgan%27s_lawsSí, los comandos dentro de
(...)
se ejecutan en una subshell.Para probar si está en un subshell, puede comparar el PID de su shell actual con el PID del shell interactivo.
Ejemplo de salida, que demuestra que los paréntesis generan una subshell y las llaves no:
fuente
()
hace subshell..perhaps de regeneración que quería decir{}
....echo $$ && ( echo $$ )
una comparación de los PID y eran los mismos, pero justo antes de enviar el comentario diciéndole que estaba equivocado, intenté otra cosa.echo $BASHPID && ( echo $BASHPID )
da diferentes PID'secho $BASHPID && { echo $BASHPID; }
da el mismo PID que sugeriste que sería el caso. Gracias por la educación$BASHPID
, esa es la otra cosa que me faltaba.