We were assigned a project that works with various file. The code in question is this:
if [[ -f $first_arg ]]; then
open_file $first_arg
elif [[ -d $first_arg ]]; then
search_directory $first_arg
.....
It works fine with just regular files, but it comes into the second condition if I run the script like this (with the ~/.) :
./script01.sh ~/.config
So I'm wondering what goes on when bash checks -f and -d, what is considered a directory or file and what is not anymore.
~/.config is quite commonly a directory as Joe has suggested in the comments.
As to what goes on, bash apparently calls stat(2) on the file in question which returns a corresponding structure including st_mode field. Details for that are in inode(7):
The stat.st_mode field (for statx(2), the statx.stx_mode field) con‐
tains the file type and mode.
Namely:
The following mask values are defined for the file type:
...
S_IFREG 0100000 regular file
...
S_IFDIR 0040000 directory
All that is left is to check which bits are set.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
Here's my script:
if [[ $(jq '.haystack | index("needle")' /etc/xyz/daemon.json) = \0 ]] ; then
jq '.haystack += ["needle"]' /etc/xyz/daemon.json > daemon.json
mv -f daemon.json /etc/xyz/daemon.json
fi
I want to add needle to haystack array in a daemon.json file. However the problem is in the if construct of the shell. When I test the condition with echo True/False the terminal does show True or False based on the command in the condition. However, I cannot do any other command, such as a simple ls or mkdir. I execute the command from terminal, not saving into a bash file. Newline doesn't seem to need \.
I'm completely new to Linux terminal, is there something I'm missing here? Thanks!
Moving your input to a function to allow testing, and replacing \0 (which has undefined behavior) with 0 (which always expands precisely to itself when given as a literal in code):
get_current_json() {
printf '%s\n' '{"haystack": ["needle", "other1", "other2"]}'
}
if [[ $(jq '.haystack | index("needle")' < <(get_current_json) ) = 0 ]] ; then
echo "Found (at position 0)"
else
echo "Not found (at position 0)"
fi
...correctly emits Found. Thus, the issue cannot be reproduced given only the code provided in the question, without further context.
One potential piece of context: If you're running this in /etc/xyz, then > daemon.json is opening the file for output with the O_TRUNC flag, and thus emptying its contents, before jq begins execution (which can happen only after its output file descriptor is connected), and thus before jq is able to read any prior values which the open(..., O_TRUNC) would delete.
To avoid this, you should use a unique name for your temporary files, as created by mktemp. (Ideally, those names should be in the same directory as the destination file, to guarantee that they don't cross filesystem boundaries and that the final mv will be atomic). See How can I use a file in a command and redirect output to the same file without truncating it?
I use to run libreoffice headless for executing some macros from a bash script:
soffice --headless "$year_to_process"/"$initials".ods macro:///Standard.C69.MergeSaveClose
Before I do that, I need to check if the macro file c69.xba is present in the directory $HOME/.config/libreoffice/4/user/basic/Standard/.
The problem is that the digit '4' above seems to me to be version dependant. Another problem is that that digit doesn't match exactly the major version number returned by libreoffice --version, which outputs:
LibreOffice 5.4.5.1 40m0(Build:1)
What I have coded so far is:
# check the macro .config/libreoffice/*/user/basic/Standard/c69.xba is present
[ -f $HOME/.config/libreoffice/*/user/basic/Standard/c69.xba ] || {
echo "$cmd: missing file basic/Standard/c69.xba"
exit 1
}
using a '*' to match any version, but when a second directory will appear, as .config/libreoffice/{4,5}/user/basic/Standard, my code will no longer work.
My question is: how can I get (from the command line) the correct path to the directory containing the macros used by the current version of libreoffice, without using a '*'?
Starting from the comment above of Aserre about the arbitrary character of the path to the configuration of LibreOffice, I chose to select the most recently created profile of LibreOffice, using the -t option of the ls command. So I found next solution to work:
# check the macro .config/libreoffice/*/user/basic/Standard/c69.xba is present
path=$(ls -dt "$HOME"/.config/libreoffice/* | head -n1) # take only first line of output of ls -dt
[ -f "$path/user/basic/Standard/c69.xba" ] || {
echo "$cmd: missing file basic/Standard/c69.xba"
exit 1
}
I'm working on a program to process requests in bash which are requested by users in a WebInterface. To give users flexibility they can specify several parameters per each job, at the end the request is saved in a file with a specific name, so the bash script could perform the requested task.
This file at the end is filled like this:
ENVIRONMENT="PRO"
INTEGRATION="J050_provisioning"
FILE="*"
DIRECTORY="out"
So the bash script will source this file to perform the needed tasks user requested. And it works great so far, but I see a security issue with this, if user enters malicious data, something like:
SOMEVAR="GONNAHACK $(rm -f some_important_file)"
OTHERVAR="DANGEROUZZZZZZ `while :; do sleep 10 & done`"
This will cause undesirable effects when sourcing the file :). Is there a way to prevent a source file execute any code but variable initializations? Or the only way would be just grep the source file before sourcing it to check it is not dangerous?
Just do not source it. Make it a configuration file composed of name=value lines (without the double quotes), read each name/value pair and assign value to name. In order not to overwrite critical variables like PATH, prefix the name with CONF_ for example.
Crude code:
while IFS='=' read -r conf_name conf_value; do
printf -v "CONF_$conf_name" '%s' "$conf_value" \
|| echo "Invalid configuration name '$conf_name'" >&2
done < your_configuration_file.conf
Test it works:
$ echo "${!CONF_*}"
CONF_DIRECTORY CONF_ENVIRONMENT CONF_FILE CONF_INTEGRATION CONF_OTHERVAR CONF_SOMEVAR
$ printf '%s\n' "$CONF_SOMEVAR"
GONNAHACK $(rm -f some_important_file)
I have recently just made this script:
if test -s $HOME/koolaid.txt ; then
Billz=$(grep / $HOME/koolaid.txt)
echo $Billz
else
Billz=$HOME/notkoolaid
echo $Billz
fi
if test -d $Billz ; then
echo "Ok"
else touch $Billz
fi
So basically, if the file $HOME/koolaid.txt file does NOT exist, then Billz will be set as $HOME/koolaid.txt. It then sucesfully creates the file.
However, if I do make the koolaid.txt then I get this
mkdir: cannot create directory : No such file or directory
Any help would be appreciated
Here is a difference between content of a variable and evaluated content...
if your variable contains a string $HOME/some - you need expand it to get /home/login/same
One dangerous method is eval.
bin=$(grep / ~/.rm.cfg)
eval rbin=${bin:-$HOME/deleted}
echo "==$rbin=="
Don't eval unless you're absolutely sure what you evaling...
Here are a couple things to fix:
Start your script with a "shebang," such as:
#!/bin/sh
This way the shell will know that you want to run this as a Bourne shell script.
Also, your conditional at the top of the script doesn't handle the case well in which .rm.cfg exists but doesn't contain a slash character anywhere in it. In that case the rbin variable never gets set.
Finally, try adding the line
ls ~
at the top so you can see how the shell is interpreting the tilde character; that might be the problem.
I'm trying to create a file hierarchy to store data. I want to create a folder for each data acquisition session. That folder has five subfolders, which are named below. My code attempt below gives an error, but I'm not sure how to correct it.
Code
#!/bin/sh
TRACES = "/Traces"
LFPS = '/LFPS'
ANALYSIS = '/Analysis'
NOTES = '/Notes'
SPIKES = '/Spikes'
folders=($TRACES $LFPS $ANALYSIS $NOTES $SPIKES)
for folder in "${folders[#]}"
do
mkdir $folder
done
Error
I get an error when declaring the variables. As written above, bash flips the error Command not found. If, instead, I declare the file names as TRACES = $('\Traces'), bash flips the error No such file or directory.
Remove the spaces between the variable names and the values:
#!/bin/sh
TRACES="/Traces"
LFPS='/LFPS'
ANALYSIS='/Analysis'
NOTES='/Notes'
SPIKES='/Spikes'
folders=($TRACES $LFPS $ANALYSIS $NOTES $SPIKES)
for folder in "${folders[#]}"
do
mkdir $folder
done
With spaces, bash interprets this like
COMMAND param1 param2
with = as param1
I'm taking the 'no spaces around the variable assignments' part of the fix as given.
Using array notation seems like overkill. Allowing for possible spaces in names, you can use:
for dir in "$TRACE" "$LFPS" "$NOTES" "$PASS"
do mkdir "$dir"
done
But even that is wasteful:
mkdir "$TRACE" "$LFPS" "$NOTES" "$PASS"
If you're worried that the directories might exist, you can avoid error messages for that with:
mkdir -p "$TRACE" "$LFPS" "$NOTES" "$PASS"
The -p option is also valuable if the paths are longer and some of the intermediate directories might be missing. If you're sure there won't be spaces in the names, the double quotes become optional (but they're safe and cheap, so you might as well use them).
Also you would want to do some checking beforehand if folders exist or not.
Also you can always debug the shell script with set -x, you could just use "mkdir -p" which would do the trick.
I made the following changes to get your script to run.
As a review comment it is unusual to create such folders hanging off the root file system.
#!/bin/sh
TRACES="/Traces"
LFPS='/LFPS'
ANALYSIS='/Analysis'
NOTES='/Notes'
SPIKES='/Spikes'
folders="$TRACES $LFPS $ANALYSIS $NOTES $SPIKES"
for folder in $folders
do
mkdir $folder
done
Spaces were removed from the initial variable assignments and I also simplified the for loop so that it iterated over the words in the folders string.