порядок выполнения логического оператора bash, ленивая оценка

Я предоставлю метод, который вы просили, а также гораздо более простой метод, если разделы не требуются. Я также буду делать примеры только для ext4, остальное должно быть возможно вывести:

Файл изображения с разделами:

#!/bin/sh

FILE=MyDrive.img

# create new 2Gb image file, will overwrite $FILE if it already exists
dd if=/dev/zero of=$FILE bs=1M count=2048

# make two 1Gb partitions and record the offsets
offset1=$(parted $FILE \
    mklabel msdos \
    mkpart primary ext2 1 1024 \
    unit B \
    print | awk '$1 == 1 {gsub("B","",$2); print $2}')
offset2=$(parted $FILE \
    mkpart primary ext2 1024 2048 \
    unit B \
    print | awk '$1 == 2 {gsub("B","",$2); print $2}')

# loop mount the partitions and record the device
loop1=$(losetup -o $offset1 -f $FILE --show)
loop2=$(losetup -o $offset2 -f $FILE --show)

# create and mount the filesystems
mkdir -p /tmp/mnt{1,2}
mkfs.ext4 $loop1
mount $loop1 /tmp/mnt1
mkfs.ext4 $loop2
mount $loop2 /tmp/mnt2

# file write test
touch /tmp/mnt1/file_on_partition_1
touch /tmp/mnt2/file_on_partition_2

# cleanup
umount /tmp/mnt1 /tmp/mnt2
losetup -d $loop1 $loop2

Файл образа без разделов:

#!/bin/sh

FILE=MyDrive.img

# create new 2Gb image file, will overwrite $FILE if it already exists
dd if=/dev/zero of=$FILE bs=1M count=2048

# create and mount filesystem
mkfs.ext4 -F $FILE
mount $FILE /mnt

# file write test
touch /tmp/mnt/file_in_imagefile

# cleanup
umount /mnt

Надеюсь, это не требует пояснений, проще было выразить этот ответ в сценарии оболочки.

6
25.10.2019, 18:02
2 ответа

Думаю, вы надеетесь, что bash это сделает:

(A && B) || (C && D) || E

но на самом деле это происходит

(A && B || C) && D || E

Где D выполняется, если либо либо B, либо C завершаются успешно.

Добавить больше группировок:

[ -e filename ] && echo filename || {
    [ -e../filename ] && echo../filename || { 
        echo 'ERROR: failed to find "filename"' 1>&2 
        exit -1
    }
}

или

{ [ -e filename ]    && echo filename   ; } || 
{ [ -e../filename ] && echo../filename; } || 
{ echo 'ERROR: failed to find "filename"' 1>&2 ;  exit -1; }

или используйте очень четкий и читаемый стиль if-elif-else, продемонстрированный @Jesse _b.


Шаг -за шагом -, это происходит:

  1. имя файла находится в текущем каталоге:

    [ -e filename ]    \   # test succeeds, status is now 0
    &&                     # status is zero, will execute this branch
    echo filename      \   # echo succeeds, status is now 0
    ||                     # status is zero, do not execute
    [ -e../filename ] \   # not executed
    &&                     # status is zero, will execute this branch
    echo../filename   \   # echo succeeds, status is now 0
    ||                     # status is zero, do not execute
    { echo; exit; }        # not executed
    
  2. имя файла находится в родительском каталоге:

    [ -e filename ]    \   # test fails, status is now 1
    &&                     # status is non-zero, do not execute this branch
    echo filename      \   # not executed
    ||                     # status is non-zero, will execute this branch
    [ -e../filename ] \   # test succeeds, status is now 0
    &&                     # status is zero, will execute this branch
    echo../filename   \   # echo succeeds, status is now 0
    ||                     # status is zero, do not execute
    { echo; exit; }        # not executed
    
6
20.08.2021, 10:54

&& и ||имеют одинаковый приоритет, поэтому:

Когда команда проходит, она ищет следующую &&и выполняет ее, даже если это не непосредственно смежный оператор. Вы должны никогда не использовать более одного из этих операторов в одном списке команд. Если требуется более одного, вы используете конструкцию if/then.

$ true && true || echo yes && echo no
no

Это сильно отличается от:

if true; then 
  true 
else 
  echo yes && echo no
fi

$ if true; then true; else echo yes && echo no; fi
$

Или:

$ true && false || echo yes && echo no
yes
no
$ if true; then false; else echo yes && echo no; fi
$

Я бы написал вашу конструкцию как:

if [ -e filename ]; then
    echo filename
elif [ -e../filename ]; then
    echo../filename
else
    echo 'ERROR: failed to find "filename"' >&2
    exit -1
fi
9
20.08.2021, 10:54

Теги

Похожие вопросы