testing parmeters in a bash script - bash

I use 4 optional parameters in a bash script:
prefix
state
node
file
I need to check, whenever prefix is used, non of remaining three parameters is used at the same time
Here what I tried in my script without success:
$ prefix=1;state=1;node=1;file=1
$ [[ -n "${prefix}" && ( -n "${state}" || -n "${node}" || -n "{file}" ) ]] && { echo "prefix must not be used with other options"; }
prefix must not be used with other options
$ unset state node file
$ [[ -n "${prefix}" && ( -n "${state}" || -n "${node}" || -n "{file}" ) ]] && { echo "prefix must not be used with other options"; }
prefix must not be used with other options
$ [[ -n "${prefix}" && -n "${state}" && -n "${node}" && -n "{file}" ]] && { echo "prefix must not be used with other options"; }
$ node=1
$ [[ -n "${prefix}" && -n "${state}" && -n "${node}" && -n "{file}" ]] && { echo "prefix must not be used with other options"; }
$ file=1
$ [[ -n "${prefix}" && -n "${state}" && -n "${node}" && -n "{file}" ]] && { echo "prefix must not be used with other options"; }
$ state=1
$ [[ -n "${prefix}" && -n "${state}" && -n "${node}" && -n "{file}" ]] && { echo "prefix must not be used with other options"; }
prefix must not be used with other options
$

Answer to my question:
$ prefix=1;state=1;node=1;file=1
$ [ \( -n "${prefix}" \) -a \( -n "${state}" -o -n "${node}" -o -n "${file}" \) ] && { echo "prefix must not be used with other options"; }
prefix must not be used with other options
$ unset state
$ [ \( -n "${prefix}" \) -a \( -n "${state}" -o -n "${node}" -o -n "${file}" \) ] && { echo "prefix must not be used with other options"; }
prefix must not be used with other options
$ unset node
$ [ \( -n "${prefix}" \) -a \( -n "${state}" -o -n "${node}" -o -n "${file}" \) ] && { echo "prefix must not be used with other options"; }
prefix must not be used with other options
$ unset file
$ [ \( -n "${prefix}" \) -a \( -n "${state}" -o -n "${node}" -o -n "${file}" \) ] && { echo "prefix must not be used with other options"; }
$

Cheap and dirty hack, assuming that you want a variable which is set, but has length zero, treat like an unset variable (you don't say this explicitly in your question, but your codes suggests it):
if [[ ${#prefix}${#state}${#node}${#file} == [1-9]*000 ]]
then
# prefix is set, the other ones are not
fi
${#x} is zero if x is unset or empty, and if prefix is set, it has at least length 1. Threfore, the catenated term above will be a number with the last three digits being zero iff state, node and file are empty, and the part of the number to the left of 000 will have at least one non-zero digit, if prefix is not empty.

Try
[ ! -z "$prefix" ] && [ -z "${state}" ] && [ -z "${node}" ] && [ -z "${file}" ] && echo "Okay" || echo "prefix must not be used with other options"
Demo :
$prefix=1;state=1;node=1;file=1
$[ ! -z "$prefix" ] && [ -z "${state}" ] && [ -z "${node}" ] && [ -z "${file}" ] && echo "Okay" || echo "prefix must not be used with other options"
prefix must not be used with other options
$unset state node file
$[ ! -z "$prefix" ] && [ -z "${state}" ] && [ -z "${node}" ] && [ -z "${file}" ] && echo "Okay" || echo "prefix must not be used with other options"
Okay
$ node=1
$[ ! -z "$prefix" ] && [ -z "${state}" ] && [ -z "${node}" ] && [ -z "${file}" ] && echo "Okay" || echo "prefix must not be used with other options"
prefix must not be used with other options
$ file=1
$[ ! -z "$prefix" ] && [ -z "${state}" ] && [ -z "${node}" ] && [ -z "${file}" ] && echo "Okay" || echo "prefix must not be used with other options"
prefix must not be used with other options
$state=1
$[ ! -z "$prefix" ] && [ -z "${state}" ] && [ -z "${node}" ] && [ -z "${file}" ] && echo "Okay" || echo "prefix must not be used with other options"
prefix must not be used with other options
$

Simply
if [ -n "$prefix" ] && [ -n "$state$node$file" ]; then
echo "prefix must not be used with other options";
fi
If $prefix is non-empty, then the concatenation of the other three must
not be non-empty.

Related

How do I use round brackets in an 'if' condition

I'm creating a bash script and somewhere inside I have this code:
if [ $# -eq 2 -a (! -r "$2" -o ! -f "$2") ]; then
echo "rvf: bestand \""$2"\" bestaat niet of is onleesbaar" 1>&2
exit 2
fi
When i try to run this inside the script I get this error:
Syntax Error (bash -n):
rvf: line 14: syntax error in conditional expression
rvf: line 14: syntax error near `-a'
rvf: line 14: `if [[ $# -eq 2 -a (! -r "$2" -o ! -f "$2") ]]; then'
How does '()' work inside Bash scripts?
[[ doens't support -a, and it is considered obsolete and non portable for [. The correct solution using [ would be
if [ "$#" -eq 2 ] && { [ ! -r "$2" ] || [ ! -f "$2" ]; }; then
Grouping is done with { ... } rather than ( ... ) to avoid creating an unnecessary subshell.
Using [[ is simplifies to
if [[ "$#" -eq 2 && ( ! -r "$2" || ! -f "$2" ) ]]; then
Parentheses can be used for grouping inside [[; as a compound command, it uses separate parsing and evaluation rules, compared to an ordinary command like [ (which is just an alias for test, not syntax of any kind).
In either case, De Morgan's laws lets you refactor this to something a little simpler:
if [ "$#" -eq 2 ] && ! { [ -r "$2" ] && [ -f "$2" ] }; then
if [[ "$#" -eq 2 && ! ( -r "$2" && -f "$2" ) ]]; then
There are multiple points of confusion here.
[ can (as an optional XSI extension to the standard) support ( as a separate word (meaning there needs to be spaces around it), but the POSIX sh specification marks it (like -a and -o) as "obsolescent" and advises against its use.
[[ does support (, but again, it needs to always be a separate word.
Don't do that at all, though. You're using only well-supported and portable functionality if you keep each test its own simple command and combine them only with the shell's boolean logic support.
That is:
if [ "$#" -eq 2 ] && { [ ! -r "$2" ] || [ ! -f "$2" ]; }; then
echo "rvf: bestand \"$2\" bestaat niet of is onleesbaar" >&2
exit 2
fi
Restructure your logic.
"Not A or Not B" is just a more complicated way to say "not (A and B)".
In bash, try
if [[ "$#" == 2 ]] && ! [[ -r "$2" && -f "$2" ]]; then
Better,
if [[ "$#" == 2 && -r "$2" && -f "$2" ]]
then : all good code
else : nope code
fi
Even better,
if [[ "$#" == 2 ]] # correct args
then if [[ -r "$2" ]] # is readable
then if [[ if -f "$2" ]] # is a file
then echo "all good"
: do all good stuff
else echo "'$2' not a file"
: do not a file stuff
fi
else echo "'$2' not readable"
: do not readable stuff
fi
else echo "Invalid number of args"
: do wrong args stuff
fi
Clear error logging is worth breaking the pieces out.
Even better, imho -
if [[ "$#" != 2 ]]
then : wrong args stuff
fi
if [[ ! -r "$2" ]]
then : unreadable stuff
fi
if [[ ! -f "$2" ]]
then : do not a file stuff
fi
: do all good stuff

Shell script works in win10, not on Linux

I am using akamai purge script from the Link and modified a little bit, so variables are in the file itself. This code successfully runs and purges object on win10, but when I run it on linux it does not purge, also it displays in console same text. I am not that fimiliar with shell scripts, any ideas what is wrong?
## Functions ------------------------------------------------------------------
function _get_property {
[ "$#" -lt 2 ] && return 1
local RC=$1
local PROP=$2
local value=$(cat ${RC} | sed "/^\s*#/d;s/\s*#[^\"']*$//" | grep ${PROP} | tail -n 1 | cut -d '=' -f2- )
if [ -z "${value}" ]; then
return 1
else
echo ${value}
return 0
fi
}
function get_properties {
local file="${1}"
declare -A aka_props
aka_props[client_secret]=$( _get_property "${file}" client_secret )
[ -z "${aka_props[client_secret]}" ] && { >&2 echo "ERROR: Please, set variable client_secret in file ${file}!!!"; exit 1; }
aka_props[client_token]=$( _get_property "${file}" client_token )
[ -z "${aka_props[client_token]}" ] && { >&2 echo "ERROR: Please, set variable client_token in file ${file}!!!"; exit 1; }
aka_props[access_token]=$( _get_property "${file}" access_token )
[ -z "${aka_props[access_token]}" ] && { >&2 echo "ERROR: Please, set variable access_token in file ${file}!!!"; exit 1; }
aka_props[host]=$( _get_property "${file}" host )
[ -z "${aka_props[host]}" ] && { >&2 echo "ERROR: Please, set variable host in file ${file}!!!"; exit 1; }
aka_props[network]=$( _get_property "${file}" network )
[ -z "${aka_props[network]}" ] && aka_props[network]="staging"
aka_props[action]=$( _get_property "${file}" action )
[ -z "${aka_props[action]}" ] && aka_props[network]="invalidate"
aka_props[type]=$( _get_property "${file}" type )
[ -z "${aka_props[type]}" ] && aka_props[network]="type"
declare -p aka_props | cut -d '=' -f2-
return 0
}
declare -A AKA_PROPS
AKA_PROPS[client_secret]=<client_secret>
AKA_PROPS[client_token]=<client_token>
AKA_PROPS[access_token]=<access_token>
AKA_PROPS[host]=<host>
AKA_PROPS[network]="production"
AKA_PROPS[action]="delete"
AKA_PROPS[type]="url"
function mk_nonce {
local s=$1
if [ -z ${s} ]; then s=$( date -u +'%Y%m%dT%H:%M:%S%z' ); fi
echo -n "${s}" | md5sum | cut -d ' ' -f1 | sed 's/.\{4\}/&-/g' | sed 's/.$//'
}
function base64_hmac_sha256 {
[ "$#" -lt 2 ] && return 1
local key=$1
local value=$2
echo -ne "${value}"| openssl sha256 -binary -hmac "${key}" | openssl base64
}
function base64_sha256 {
[ "$#" -lt 1 ] && return 1
local value=$1
echo -ne "${value}" | openssl dgst -binary -sha256 | openssl base64
}
function mk_auth_header {
[ "$#" -lt 3 ] && return 1
local -n aka_props=$1
local timestamp=$2
local nonce=$3
echo -n "EG1-HMAC-SHA256 client_token=${AKA_PROPS[client_token]};access_token=${AKA_PROPS[access_token]};timestamp=${timestamp};nonce=${nonce};"
#echo -n "EG1-HMAC-SHA256 client_token=${aka_props[client_token]};access_token=${aka_props[access_token]};timestamp=${timestamp};nonce=${nonce};"
}
function sign_data {
[ "$#" -lt 2 ] && return 1
local key=$1
local -n data_to_sign=$2
#local data="${data_to_sign[method]}\t${data_to_sign[scheme]}\t${data_to_sign[host]}\t${data_to_sign[request_uri]}\t${data_to_sign[canonical_headers]}\t${data_to_sign[hash_content]}\t${data_to_sign[auth_header]}"
local data="${data_to_sign[method]}\t${data_to_sign[scheme]}\t${data_to_sign[host]}\t${data_to_sign[request_uri]}\t\t${data_to_sign[hash_content]}\t${data_to_sign[auth_header]}"
base64_hmac_sha256 "${key}" "${data}"
}
function mk_body {
local type="${1}"
local objects="${2}"
local domain="${3}"
local arr_objects
local objs
IFS=',' read -r -a arr_objects <<< "${objects}"
for i in ${!arr_objects[#]}
do
local tmp=$( echo ${arr_objects[i]} | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's,/,\\/,g' )
if [ "${type}" == "cpcode" ]; then
objs="${objs},${tmp}"
else
objs="${objs},\"${tmp}\""
fi
done
objs=$( echo "${objs}" | sed 's/^,//' )
if [ "${type}" == "url" ]; then
echo "{\"hostname\":\"${domain}\",\"objects\":[${objs}]}"
else
echo "{\"objects\":[${objs}]}"
fi
}
## Options --------------------------------------------------------------------
P_NAME=${0##*\/}
#CONF=./.akarc
#[ ! -f ${CONF} ] && CONF=./.akarc
#[ ! -f ${CONF} ] && { >&2 echo "ERROR: configuration file not found (~/.akarc or /etc/.akarc)"; exit 1; }
#declare -A AKA_PROPS=$( get_properties "${CONF}" );
usage() {
cat << EOF
usage: ${P_NAME} [OPTIONS] -o <obj1[,obj2,obj3,...]>
Purge the Akamai cache via CCU REST API v3.
REMARKS
Please create a config file in ~/.akarc or /etc/.akarc
PARAMETERS:
-o | --objects List of objects to purge (with comma separated values)
OPTIONS:
-t | --type Type of objects
Possible values: url, cpcode or tag
Default: ${AKA_PROPS[type]}
-d | --domain Domain site (es. your.domain.com).
To use with type=url
-a | --action The desired action to manage the cache
Possible values: invalidate or delete
Default: ${AKA_PROPS[action]}
-n | --network The network on which you want to invalidate or delete content
Possible values: staging or production
Default: ${AKA_PROPS[network]}
-h | --help Show this message
-v | --version Show version
CONFIG FILE:
In the config file the following values have to been declared:
client_secret = <your client secret>
client_token = <your client token>
access_token = <your access token>
host = <your akamai host>
There is the possibility to set the default values:
network = <staging | production>
action = <invalidate | delete>
type = <url | cpcode | tag>
If no values are declared the default ones are:
network = staging
action = invalidate
type = url
EOF
}
ARGS=$( getopt -o "d:t:o:a:n:hv" -l "domain:,type:,objects:,action:,network:,help,version" -n "$0" -- "$#" )
eval set -- "$ARGS"
VERSION=0.2
NETWORK=${AKA_PROPS[network]}
ACTION=${AKA_PROPS[action]}
TYPE=${AKA_PROPS[type]}
while true; do
case "$1" in
-d | --domain )
DOMAIN=${2}
shift 2
;;
-t | --type )
TYPE=${2}
shift 2
;;
-o | --objects )
OBJECTS=${2}
shift 2
;;
-a | --action )
ACTION=${2}
shift 2
;;
-n | --network )
NETWORK=${2}
shift 2
;;
-h | --help )
usage
exit 0
;;
-v | --version )
echo $0 version: $VERSION
exit 0
;;
-- )
shift
break
;;
* )
>&2 echo "Internal error!"
exit 1
;;
esac
done
## Main -----------------------------------------------------------------------
[ -z "${TYPE}" ] || [ -z "${OBJECTS}" ] && { usage; exit 1; }
[ "${TYPE}" == "url" ] && [ -z "${DOMAIN}" ] && { usage; exit 1; }
[ "${TYPE}" != "url" ] && [ ! -z "${DOMAIN}" ] && { echo "WARNING: type is different of url then -d,--domain will be ignored ..."; }
[ "${TYPE}" != "url" ] && [ "${TYPE}" != "cpcode" ] && [ "${TYPE}" != "tag" ] && { >&2 echo "ERROR: Possible value of OBJECT TYPE is url, cpcode or tag"; exit 1; }
[ "${NETWORK}" != "staging" ] && [ "${NETWORK}" != "production" ] && { >&2 echo "ERROR: Possible value of NETWORK is staging or production"; exit 1; }
[ "${ACTION}" != "invalidate" ] && [ "${ACTION}" != "delete" ] && { >&2 echo "ERROR: Possible value of ACTION is invalidate or delete"; exit 1; }
BODY=$( mk_body "${TYPE}" "${OBJECTS}" "${DOMAIN}" )
[ $(echo -ne "${BODY}" | wc -c ) -gt 131072 ] && { >&2 echo "ERROR: The body size is greater than 131072!!!" exit 1; }
TIMESTAMP=$( date -u +'%Y%m%dT%H:%M:%S%z' )
NONCE=$( mk_nonce ${TIMESTAMP} )
SIGN_KEY=$( base64_hmac_sha256 ${AKA_PROPS[client_secret]} ${TIMESTAMP} )
AUTH_HEADER=$( mk_auth_header AKA_PROPS ${TIMESTAMP} ${NONCE} )
declare -A DATA_TO_SIGN=( [method]="POST"
[scheme]="https"
[host]="${AKA_PROPS[host]}"
[request_uri]="/ccu/v3/${ACTION}/${TYPE}/${NETWORK}"
[hash_content]=$( base64_sha256 "${BODY}" )
[auth_header]=$( mk_auth_header AKA_PROPS ${TIMESTAMP} ${NONCE}) )
SIGNED_DATA=$( sign_data "${SIGN_KEY}" DATA_TO_SIGN )
SIGNED_AUTH_HEADER="Authorization: ${AUTH_HEADER}signature=${SIGNED_DATA}"
H_JSON="Content-Type: application/json"
echo curl -s -H \"Expect:\" \
-H \"User-Agent:${P_NAME}\" \
-H \"Accept:${H_JSON}\" \
-H \"${H_JSON}\" \
-H \"${SIGNED_AUTH_HEADER}\" \
-X POST -d \'${BODY}\' \"${DATA_TO_SIGN[scheme]}://${DATA_TO_SIGN[host]}${DATA_TO_SIGN[request_uri]}\" | bash -x | jq .
exit 0
In
AKA_PROPS[client_secret]=<client_secret>
Hope you're replacing <client_secret> with its actual value as in Linux, the </> is meant for redirection. Apart from that, I would suggest below things.
Add a shebang(#!/bin/bash) to the top of the file
Then run dos2unix script_name
Finally run the script.
Associative arrays like ${AKA_PROPS[network]} are available for bash 4.0 or newer so first hing to do is to check bash version on your Linux against the version on Window$.
bash --version
GNU bash, version 4.3.42(1)-release (x86_64-suse-linux-gnu)
Another good thing to do is to add your changes after line 119 on the original script. Make sure values are properly quoted and that all values in .akarc file are present in your script.
#declare -A AKA_PROPS=$( get_properties "${CONF}" );
declare -A AKA_PROPS
AKA_PROPS[client_secret]=<client_secret>
AKA_PROPS[client_token]=<client_token>
AKA_PROPS[access_token]=<access_token>
AKA_PROPS[host]=<host>
AKA_PROPS[network]="production"
AKA_PROPS[action]="delete"
AKA_PROPS[type]="url"
You can test with -h option since AKA_PROPS values will be shown in the help text (e.g. Default: ${AKA_PROPS[type]}).

Parameter from URL into shell script

How is it possible to get an URL parameter like /?photo=1.png into a shell script as a variable, running into a cgi-bin container on apache?
Edit
Iam generating a list of all files in a directory.
#!/bin/bash
echo "Content-type: text/html"
echo
for file in /var/www/html/export/tui/*;
do
echo "<a href='/cgi-bin/test.cgi?file="${file: -27}"'>"${file: -27}"</a><br>";
done;
Now, i want to give the file name as a parameter into a second script, who needs this for reading it.
I found a solution who take the URL parameter and give it into my script
#!/bin/bash
echo "Content-type: text/html"
echo
function cgi_decodevar()
{
[ $# -ne 1 ] && return
local v t h
t="${1//+/ }%%"
while [ ${#t} -gt 0 -a "${t}" != "%" ]; do
v="${v}${t%%\%*}" # digest up to the first %
t="${t#*%}" # remove digested part
if [ ${#t} -gt 0 -a "${t}" != "%" ]; then
h=${t:0:2}
t="${t:2}"
v="${v}"`echo -e \\\\x${h}`
fi
done
echo "${v}"
return
}
function cgi_getvars()
{
[ $# -lt 2 ] && return
local q p k v s
case $1 in
GET)
[ ! -z "${QUERY_STRING}" ] && q="${QUERY_STRING}&"
;;
POST)
cgi_get_POST_vars
[ ! -z "${QUERY_STRING_POST}" ] && q="${QUERY_STRING_POST}&"
;;
BOTH)
[ ! -z "${QUERY_STRING}" ] && q="${QUERY_STRING}&"
cgi_get_POST_vars
[ ! -z "${QUERY_STRING_POST}" ] && q="${q}${QUERY_STRING_POST}&"
;;
esac
shift
s=" $* "
while [ ! -z "$q" ]; do
p="${q%%&*}"
k="${p%%=*}"
v="${p#*=}"
q="${q#$p&*}"
[ "$1" = "ALL" -o "${s/ $k /}" != "$s" ] && \
export "$k"="`cgi_decodevar \"$v\"`"
done
return
}
cgi_getvars BOTH ALL
echo $foo

Chosing "input" in an AND (&&) and OR (||) list of commands

I have to find a way to have my script read from one of these three options:
a file argument
standard input
a previously established environment variable
Here's what I currently have:
#!/bin/bash
key=$1
[ $# -ge 1 -a -f "$2" ] && input="$2" || [ -f "$INPUT" ] && input="$INPUT" || input="-"
echo $input
Only the environment variable refuses to work, the rest works fine.
I've tried using the export INPUT="pathnametofile" before but it doesn't make any difference, I end up with the shell asking me to enter info as if I called on cat.
The problem in your script
Your attemp is not working due to the way the shell processes a Lists of Commands:
‘&&’ and ‘||’ have equal precedence.
AND and OR lists are executed with left associativity.
Your sentence:
[ $# -ge 1 -a -f "$2" ] && input="$2" || [ -f "$INPUT" ] && input="$INPUT" || input="-"
does the same as follows:
[ $# -ge 1 -a -f "$2" ] && input="$2"
[ $? -eq 0 ] || [ -f "$INPUT" ]
[ $? -eq 0 ] && input="$INPUT"
[ $? -eq 0 ] || input="-"
Now yo may see why your unexpected behaviour.
A better attempt grouping commands
{ [ $# -ge 1 -a -f "$2" ] && input="$2"; } || { [ -f "$INPUT" ] && input="$INPUT"; } || input="-"
Now, due to precedence, the first group is not needed at all:
[ $# -ge 1 -a -f "$2" ] && input="$2" || { [ -f "$INPUT" ] && input="$INPUT"; } || input="-"
Furthermore, unless you have set the positional parameters by hand, you can remove the first check (after all, if $2 is emtpy, -f "" fails the same).
[ -f "$2" ] && input="$2" || { [ -f "$INPUT" ] && input="$INPUT"; } || input="-"
An alternative with the if conditional construct
if [ -f "$2" ]; then
input=$2
elif [ -f "$INPUT" ]; then
input=$INPUT
fi
echo "${input:=-}"
untested, but you'll probably have better luck with if commands, and test that the variable is not empty:
if [ $# -ge 1 -a -f "$2" ]; then
input="$2"
elif [ -n "$INPUT" -a -f "$INPUT" ]; then
input="$INPUT"
else
input="-"
fi

Test multiple file conditions in one swoop (BASH)?

Often when writing for the bash shell, one needs to test if a file (or Directory) exists (or doesn't exist) and take appropriate action. Most common amongst these test are...
-e - file exists, -f - file is a regular file (not a directory or device file), -s - file is not zero size, -d - file is a directory, -r - file has read permission, -w - file has write, or -x execute permission (for the user running the test)
This is easily confirmed as demonstrated on this user-writable directory....
#/bin/bash
if [ -f "/Library/Application Support" ]; then
echo 'YES SIR -f is fine'
else echo 'no -f for you'
fi
if [ -w "/Library/Application Support" ]; then
echo 'YES SIR -w is fine'
else echo 'no -w for you'
fi
if [ -d "/Library/Application Support" ]; then
echo 'YES SIR -d is fine'
else echo 'no -d for you'
fi
➝ no -f for you ✓
➝ YES SIR -w is fine ✓
➝ YES SIR -d is fine ✓
My question, although seemingly obvious, and unlikely to be impossible - is how to simply combine these tests, without having to perform them separately for each condition... Unfortunately...
if [ -wd "/Library/Application Support" ]
▶ -wd: unary operator expected
if [ -w | -d "/Library/Application Support" ]
▶ [: missing `]'
▶ -d: command not found
if [ -w [ -d "/Library.... ]] & if [ -w && -d "/Library.... ]
▶ [: missing `]'
➝ no -wd for you ✖
➝ no -w | -d for you ✖
➝ no [ -w [ -d .. ]] for you ✖
➝ no -w && -d for you ✖
What am I missing here?
You can use logical operators to multiple conditions, e.g. -a for AND:
MYFILE=/tmp/data.bin
if [ -f "$MYFILE" -a -r "$MYFILE" -a -w "$MYFILE" ]; then
#do stuff
fi
unset MYFILE
Of course, you need to use AND somehow as Kerrek(+1) and Ben(+1) pointed it out. You can do in in few different ways. Here is an ala-microbenchmark results for few methods:
Most portable and readable way:
$ time for i in $(seq 100000); do [ 1 = 1 ] && [ 2 = 2 ] && [ 3 = 3 ]; done
real 0m2.583s
still portable, less readable, faster:
$ time for i in $(seq 100000); do [ 1 = 1 -a 2 = 2 -a 3 = 3 ]; done
real 0m1.681s
bashism, but readable and faster
$ time for i in $(seq 100000); do [[ 1 = 1 ]] && [[ 2 = 2 ]] && [[ 3 = 3 ]]; done
real 0m1.285s
bashism, but quite readable, and fastest.
$ time for i in $(seq 100000); do [[ 1 = 1 && 2 = 2 && 3 = 3 ]]; done
real 0m0.934s
Note, that in bash, "[" is a builtin, so bash is using internal command not a symlink to /usr/bin/test exacutable. The "[[" is a bash keyword. So the slowest possible way will be:
time for i in $(seq 100000); do /usr/bin/\[ 1 = 1 ] && /usr/bin/\[ 2 = 2 ] && /usr/bin/\[ 3 = 3 ]; done
real 14m8.678s
You want -a as in -f foo -a -d foo (actually that test would be false, but you get the idea).
You were close with & you just needed && as in [ -f foo ] && [ -d foo ] although that runs multiple commands rather than one.
Here is a manual page for test which is the command that [ is a link to. Modern implementations of test have a lot more features (along with the shell-builtin version [[ which is documented in your shell's manpage).
check-file(){
while [[ ${#} -gt 0 ]]; do
case $1 in
fxrsw) [[ -f "$2" && -x "$2" && -r "$2" && -s "$2" && -w "$2" ]] || return 1 ;;
fxrs) [[ -f "$2" && -x "$2" && -r "$2" && -s "$2" ]] || return 1 ;;
fxr) [[ -f "$2" && -x "$2" && -r "$2" ]] || return 1 ;;
fr) [[ -f "$2" && -r "$2" ]] || return 1 ;;
fx) [[ -f "$2" && -x "$2" ]] || return 1 ;;
fe) [[ -f "$2" && -e "$2" ]] || return 1 ;;
hf) [[ -h "$2" && -f "$2" ]] || return 1 ;;
*) [[ -e "$1" ]] || return 1 ;;
esac
shift
done
}
check-file fxr "/path/file" && echo "is valid"
check-file hf "/path/folder/symlink" || { echo "Fatal error cant validate symlink"; exit 1; }
check-file fe "file.txt" || touch "file.txt" && ln -s "${HOME}/file.txt" "/docs/file.txt" && check-file hf "/docs/file.txt" || exit 1
if check-file fxrsw "${HOME}"; then
echo "Your home is your home from the looks of it."
else
echo "You infected your own home."
fi
Why not write a function to do it?
check_file () {
local FLAGS=$1
local PATH=$2
if [ -z "$PATH" ] ; then
if [ -z "$FLAGS" ] ; then
echo "check_file: must specify at least a path" >&2
exit 1
fi
PATH=$FLAGS
FLAGS=-e
fi
FLAGS=${FLAGS#-}
while [ -n "$FLAGS" ] ; do
local FLAG=`printf "%c" "$FLAGS"`
if [ ! -$FLAG $PATH ] ; then false; return; fi
FLAGS=${FLAGS#?}
done
true
}
Then just use it like:
for path in / /etc /etc/passwd /bin/bash
{
if check_file -dx $path ; then
echo "$path is a directory and executable"
else
echo "$path is not a directory or not executable"
fi
}
And you should get:
/ is a directory and executable
/etc is a directory and executable
/etc/passwd is not a directory or not executable
/bin/bash is not a directory or not executable
This seems to work (notice the double brackets):
#!/bin/bash
if [[ -fwd "/Library/Application Support" ]]
then
echo 'YES SIR -f -w -d are fine'
else
echo 'no -f or -w or -d for you'
fi

Resources