I'm running a command with output based on some files. So I cat
the files inside the command. However, when there's nothing in the file, this is problematic
mkbootimg --kernel "$SCRIPT_DIR/devices/$DEVICE/kernel/k/arch/arm64/boot/Image" \
--ramdisk boot.img-ramdisk \
--dtb boot.img-dtb \
--cmdline "$(cat boot.img-cmdline)" \
--base `cat boot.img-base` \
--kernel_offset `cat boot.img-kernel_offset` \
--ramdisk_offset `cat boot.img-ramdisk_offset` \
--tags_offset `cat boot.img-tags_offset` \
--dtb_offset `cat boot.img-dtb_offset` \
--os_version `cat boot.img-os_version` \
--os_patch_level `cat boot.img-os_patch_level` \
--pagesize `cat boot.img-pagesize` \
--header_version `cat boot.img-header_version` \
--hashtype `cat boot.img-hashtype` \
--board `cat boot.img-board` \
--ramdisk_offset `cat boot.img-ramdisk_offset` \
-o ../boot.img)
For example, if there's nothing on boot.img-board
then it does
--board --ramdisk_offset 0x10000
and thus it thinks that the board
is --ramdisk_offset
and then interprets 0x10000
as another argument for mkbootimg
.
What is the best way to solve this problem? Howe can I only do --board
when there's actually something inside boot.img-board
?
PS: I want to do this for all arguments, so maybe a function that returns the argument would make more sense here.
UPDATE:
What if the program really expects something in --OPTION so you should only pass if there's something there?
CodePudding user response:
If you replace --OPTION `cat FILE`
with --OPTION "$(< FILE)"
then the option will get an empty string as an argument if the file is empty. Using the quotes also means that option arguments containing spaces, or other problematic characters, will be handled correctly. Shellcheck finds problems like this, and offers advice on how to fix them. (I replaced `...`
with $(...)
(as advised by Shellcheck) because the second form has several advantages. I replaced $(cat FILE)
with $(< FILE)
because (with Bash) the effect is the same but the second form avoids running a subprocess.)
CodePudding user response:
bash has a fairly easy way to include options only if a variable is nonempty (${var: "--option" "$var"}
-- see my answer here), so one possible solution would be to read the files' contents into variables first and then use this. But I think there's a simpler way.
What I'd do to solve this is to create a list of options and their values in an array, and add each option-value pair only after checking whether the file exists & is not empty:
options=() # Start with an empty array
# then add an option for each nonempty file
[[ -s boot.img-cmdline ]] && options =(--cmdline "$(< boot.img-cmdline)")
[[ -s boot.img-base ]] && options =(--base "$(< boot.img-base)")
... etc ...
mkbootimg --kernel "$SCRIPT_DIR/devices/$DEVICE/kernel/k/arch/arm64/boot/Image" \
--ramdisk boot.img-ramdisk \
--dtb boot.img-dtb \
"${options[@]}" \
-o ../boot.img)
Note that using arrays properly involves a lot of random-looking syntax (parentheses, braces, brackets, quotes, etc), and none of it is optional (well, not if you want it to work right).
But I think there's an even simpler way. Since each of the option names seems to be identical to the filename suffixes, you could use a simple loop to populate the array:
options=()
for opt in cmdline base kernel_offset ramdisk_offset \
tags_offset dtb_offset os_version os_patch_level \
pagesize header_version hashtype board ramdisk_offset
do
[[ -s "boot.img-$opt" ]] && options =("--$opt" "$(< "boot.img-$opt")")
done
...then use "${options[@]}" as above