bash command to check if a directory is executable - linux

My goal is to check if the the execution bit is not set for a directory.
I changed the permission of /tmp so that the execution bit is off.
root$: chmod 666 /tmp
root$: ls -l /
....
.....
drw-rw-rw- 12 root root 4096 Feb 29 15:17 tmp
In my bash script, I have tried the following without success:
if [ ! -x /tmp ]; then
......
I have experimented with all the suggestions at the following link, but the only different syntax approach does not work for me either:
if [[ ! -x /tmp] ; then
check if a file is executable
These work as expected for regular files, but not for any directory, but I don't know why. Any ideas?
Update #2
I wrote a mini bash script with only the code suggested in a comment below.
Results:
[root#mc/]# cat tst.sh
#!/bin/bash
if [ ! -x /tmp ]; then echo 'not executable!'; fi
exit
[root#mc/]# ./tst.sh
[root#mc/]#

All of the code that you have provided in your question is correct (I just finished testing it myself). It stands to reason, therefore, that something else in the script that is failing. If you could, try running this simplified snippet:
if [ ! -x /tmp ]; then echo 'not executable!'; fi
As a quick side note, the "executable" flag for directories in Unix systems does not actually mean "executable". It is actually the way that the directory is marked as searchable. While I'm not sure if this will help with the problem you are working on, it is an interesting usage of existing fields.

You can perhaps use the find command to single out any directory without the executable bit
notex=$(find . -type d -maxdepth 1 -perm 666)
I think that may help..

Related

How can I get the path to the currently running shell script (cshell linux)

Linux/CSHELL:
How can I get the path to a tcsh script that is being sourced?
This is not $PWD. Rather, the path to the script (file).
Versions/specifics:
% echo $SHELL $PLATFORM
/tool/pandora/bin/tcsh linux_3.10.0_64
ls -l /tool/pandora/bin/tcsh
lrwxrwxrwx 1 pandora pandora 33 Oct 20 2016 /tool/pandora/bin/tcsh -> ../.package/tcsh-6.19.00/bin/tcsh
There are lots of notes in here for this type of thing. But I didn't see one for linux c-shell.
Thanks in Advance !
Example:
I have a file /home/myid/my_cshell.csh...
set a = `readlink -f ${0}`
echo $a
echo ${0}
source /home/myid/my_cshell.csh
/tool/pandora/.package/tcsh-6.19.00/bin/tcsh
/tool/pandora/bin/tcsh
This is giving me the path to tcsh, not /home/myid/my_cshell.csh
The script above works fine if I tcsh it, but not source it.
I need the path to the file being sourced.
assuming you have readlink available:
readlink -f "${0}"
should work. ${0} will give you the relative path, and readlink -f will resolve it to the absolute path.
There must already be some way to reference the file... If not, how could it be sourced in the first place? So I suppose the problem is that ${0} referecnes the parent's file system location, and not the sourced file. In that case, readlink -f <file> should still work, but maybe try calling it first, and then pass the result when you run source. Like this:
main.csh:
#!/bin/csh
set p=`readlink -f source.csh`
source source.csh "${p}"
source.csh:
#!/bin/csh
set pmain=`readlink -f "${0}"`
echo "my path is ${1}. main.csh's path is ${pmain}"

Using find in shell script

I need to check if a given string in a bash script is a command.
In other words: I need to check if that String is a filename in the /bin directory (Only /bin).
I tried
echo "Write a bash command: "
read -r var2
if [[ -z (find /bin -name $var2) ]]
then echo "That's not a command" && exit 1
fi
But it didn't work.
Ideas?
EDIT: Solved. As amdixon suggested I changed (find /bin -name $var2) for $(find /bin -name $var2).
Thanks dude.
Depending on your actual requirements, it can be easier than that:
if ! [ -x /bin/"$var2" ]
then
echo "That's not a command" && exit 1
fi
[ is short for the test command and with the -x argument, it will return 0 (true) if the given file is executable by you. Note that this will exclude commands that are executable by other users only because you have insufficient permissions.
If you use the -f argument instead, [ will test for any file in the /bin directory, whether it is executable or not (of course usually all of them are):
if ! [ -f /bin/"$var2" ]
then
echo "That's not a command" && exit 1
fi
If you need to make sure that the file is executable (even if it may not be executable by you), see this question for a solution using file.
Type help test on the command line to read more about possible arguments for [.
echo "Write a bash command: "
read -r var2
if [ ! -f /bin/"$var2" ]
then echo "That's not a command" && exit 1
fi
is the natural way to do this in bash. [ expr ] is the shorthand for the builtin test command. Type man builtins and scroll until you find test for a complete description of what testcan do for you. For instance, if you prefer testing simultaneously if the file exists and is executable, you can replace:
[ ! -f /bin/"$var2" ]
by:
[ ! -x /bin/"$var2" ]
I need to check if a given string in a bash script is a command.
vs
I need to check if that String is a filename in the /bin directory (Only /bin).
This is by no means the same. I guess you refer to well-known "shell commands" and in this case, There are three reasons you might be on the wrong path:
The root hierarchy is for everything needed to boot up the system. This might include what you consider "commands", but other stuff as well (like e.g. lvm or cryptsetup)
For the same reason, binaries you would consider "commands" might be missing from /bin, living in /usr/bin instead.
Shells have "builtin" commands and there is no guarantee you will find them as separate binaries at all.
Given all that, if you still want to look for executables in /bin, there is really no reason to use find at all. test (or the abbreviated version just writing brackets) will be enough, like if [ -x /bin/$var2 ]; then ...

what does this shell script codes means

i need some help to understand the following few lines of a given shell script.
here $_filecount variable hold the number of file to be archived
here i want to know what $TARC means, searched on this command but got no result for TARC and TARU commands. could anybody explain me what these commands are
_archive=${ARCHIVE_PATH}/${_name}_$(hostname)_${_today}.tar
if [ $_filecount -ne 0 ]; then
if ! [ -f ${_archive} ]; then
touch ${ARCHIVE_PATH}/${_today}
$TARC ${_archive} -C ${ARCHIVE_PATH} ${_today}
rm -f ${ARCHIVE_PATH}/${_today}
fi
for i in ${_filelist}; do
$TARU ${_archive} -C ${_path} $i
[ $? -eq 0 ] && rm -f ${_path}/$i
done
fi
when this code is run using cygwin at line $TARC ${_archive} -c ${ARCHIVE_PATH} ${_today} returns following error
tar: invalid option -- 'E'
Try `tar --help' or `tar --usage' for more information.
thanks in advance for any help
$TARC and $TARU are variables (if they aren't defined in your script somewhere, then they must be environment variables)
Try echo $TARC to see what they are set to.
Looks TARC is the tar command to archive and TARU is the tar command to unarchive.
TARC and TARU must be set somewhere or else you would get a different error - the error you are seeing is tar specific.

How to get full path of a file?

Is there an easy way I can print the full path of file.txt ?
file.txt = /nfs/an/disks/jj/home/dir/file.txt
The <command>
dir> <command> file.txt
should print
/nfs/an/disks/jj/home/dir/file.txt
Use readlink:
readlink -f file.txt
I suppose you are using Linux.
I found a utility called realpath in coreutils 8.15.
realpath -s file.txt
/data/ail_data/transformed_binaries/coreutils/test_folder_realpath/file.txt
Since the question is about how to get the full/absolute path of a file and not about how to get the target of symlinks, use -s or --no-symlinks which means don't expand symlinks.
As per #styrofoam-fly and #arch-standton comments, realpath alone doesn't check for file existence, to solve this add the e argument: realpath -e file
The following usually does the trick:
echo "$(cd "$(dirname "$1")" && pwd -P)/$(basename "$1")"
I know there's an easier way that this, but darned if I can find it...
jcomeau#intrepid:~$ python -c 'import os; print(os.path.abspath("cat.wav"))'
/home/jcomeau/cat.wav
jcomeau#intrepid:~$ ls $PWD/cat.wav
/home/jcomeau/cat.wav
On Windows:
Holding Shift and right clicking on a file in Windows Explorer gives you an option called Copy as Path.
This will copy the full path of the file to clipboard.
On Linux:
You can use the command realpath yourfile to get the full path of a file as suggested by others.
find $PWD -type f | grep "filename"
or
find $PWD -type f -name "*filename*"
If you are in the same directory as the file:
ls "`pwd`/file.txt"
Replace file.txt with your target filename.
I know that this is an old question now, but just to add to the information here:
The Linux command which can be used to find the filepath of a command file, i.e.
$ which ls
/bin/ls
There are some caveats to this; please see https://www.cyberciti.biz/faq/how-do-i-find-the-path-to-a-command-file/.
You could use the fpn (full path name) script:
% pwd
/Users/adamatan/bins/scripts/fpn
% ls
LICENSE README.md fpn.py
% fpn *
/Users/adamatan/bins/scripts/fpn/LICENSE
/Users/adamatan/bins/scripts/fpn/README.md
/Users/adamatan/bins/scripts/fpn/fpn.py
fpn is not a standard Linux package, but it's a free and open github project and you could set it up in a minute.
Works on Mac, Linux, *nix:
This will give you a quoted csv of all files in the current dir:
ls | xargs -I {} echo "$(pwd -P)/{}" | xargs | sed 's/ /","/g'
The output of this can be easily copied into a python list or any similar data structure.
echo $(cd $(dirname "$1") && pwd -P)/$(basename "$1")
This is explanation of what is going on at #ZeRemz's answer:
This script get relative path as argument "$1"
Then we get dirname part of that path (you can pass either dir or file to this script): dirname "$1"
Then we cd "$(dirname "$1") into this relative dir
&& pwd -P and get absolute path for it. -P option will avoid all symlinks
After that we append basename to absolute path: $(basename "$1")
As final step we echo it
You may use this function. If the file name is given without relative path, then it is assumed to be present in the current working directory:
abspath() { old=`pwd`;new=$(dirname "$1");if [ "$new" != "." ]; then cd $new; fi;file=`pwd`/$(basename "$1");cd $old;echo $file; }
Usage:
$ abspath file.txt
/I/am/in/present/dir/file.txt
Usage with relative path:
$ abspath ../../some/dir/some-file.txt
/I/am/in/some/dir/some-file.txt
With spaces in file name:
$ abspath "../../some/dir/another file.txt"
/I/am/in/some/dir/another file.txt
You can save this in your shell.rc or just put in console
function absolute_path { echo "$PWD/$1"; }
alias ap="absolute_path"
example:
ap somefile.txt
will output
/home/user/somefile.txt
I was surprised no one mentioned located.
If you have the locate package installed, you don't even need to be in the directory with the file of interest.
Say I am looking for the full pathname of a setenv.sh script. This is how to find it.
$ locate setenv.sh
/home/davis/progs/devpost_aws_disaster_response/python/setenv.sh
/home/davis/progs/devpost_aws_disaster_response/webapp/setenv.sh
/home/davis/progs/eb_testy/setenv.sh
Note, it finds three scripts in this case, but if I wanted just one I
would do this:
$ locate *testy*setenv.sh
/home/davis/progs/eb_testy/setenv.sh
This solution uses commands that exist on Ubuntu 22.04, but generally exist on most other Linux distributions, unless they are just to hardcore for s'mores.
The shortest way to get the full path of a file on Linux or Mac is to use the ls command and the PWD environment variable.
<0.o> touch afile
<0.o> pwd
/adir
<0.o> ls $PWD/afile
/adir/afile
You can do the same thing with a directory variable of your own, say d.
<0.o> touch afile
<0.o> d=/adir
<0.o> ls $d/afile
/adir/afile
Notice that without flags ls <FILE> and echo <FILE> are equivalent (for valid names of files in the current directory), so if you're using echo for that, you can use ls instead if you want.
If the situation is reversed, so that you have the full path and want the filename, just use the basename command.
<0.o> touch afile
<0.o> basename $PWD/afile
afile
In a similar scenario, I'm launching a cshell script from some other location. For setting the correct absolute path of the script so that it runs in the designated directory only, I'm using the following code:
set script_dir = `pwd`/`dirname $0`
$0 stores the exact string how the script was executed.
For e.g. if the script was launched like this: $> ../../test/test.csh,
$script_dir will contain /home/abc/sandbox/v1/../../test
For Mac OS X, I replaced the utilities that come with the operating system and replaced them with a newer version of coreutils. This allows you to access tools like readlink -f (for absolute path to files) and realpath (absolute path to directories) on your Mac.
The Homebrew version appends a 'G' (for GNU Tools) in front of the command name -- so the equivalents become greadlink -f FILE and grealpath DIRECTORY.
Instructions for how to install the coreutils/GNU Tools on Mac OS X through Homebrew can be found in this StackExchange arcticle.
NB: The readlink -f and realpath commands should work out of the box for non-Mac Unix users.
I like many of the answers already given, but I have found this really useful, especially within a script to get the full path of a file, including following symlinks and relative references such as . and ..
dirname `readlink -e relative/path/to/file`
Which will return the full path of the file from the root path onwards.
This can be used in a script so that the script knows which path it is running from, which is useful in a repository clone which could be located anywhere on a machine.
basePath=`dirname \`readlink -e $0\``
I can then use the ${basePath} variable in my scripts to directly reference other scripts.
Hope this helps,
Dave
This worked pretty well for me. It doesn't rely on the file system (a pro/con depending on need) so it'll be fast; and, it should be portable to most any *NIX. It does assume the passed string is indeed relative to the PWD and not some other directory.
function abspath () {
echo $1 | awk '\
# Root parent directory refs to the PWD for replacement below
/^\.\.\// { sub("^", "./") } \
# Replace the symbolic PWD refs with the absolute PWD \
/^\.\// { sub("^\.", ENVIRON["PWD"])} \
# Print absolute paths \
/^\// {print} \'
}
This is naive, but I had to make it to be POSIX compliant. Requires permission to cd into the file's directory.
#!/bin/sh
if [ ${#} = 0 ]; then
echo "Error: 0 args. need 1" >&2
exit 1
fi
if [ -d ${1} ]; then
# Directory
base=$( cd ${1}; echo ${PWD##*/} )
dir=$( cd ${1}; echo ${PWD%${base}} )
if [ ${dir} = / ]; then
parentPath=${dir}
else
parentPath=${dir%/}
fi
if [ -z ${base} ] || [ -z ${parentPath} ]; then
if [ -n ${1} ]; then
fullPath=$( cd ${1}; echo ${PWD} )
else
echo "Error: unsupported scenario 1" >&2
exit 1
fi
fi
elif [ ${1%/*} = ${1} ]; then
if [ -f ./${1} ]; then
# File in current directory
base=$( echo ${1##*/} )
parentPath=$( echo ${PWD} )
else
echo "Error: unsupported scenario 2" >&2
exit 1
fi
elif [ -f ${1} ] && [ -d ${1%/*} ]; then
# File in directory
base=$( echo ${1##*/} )
parentPath=$( cd ${1%/*}; echo ${PWD} )
else
echo "Error: not file or directory" >&2
exit 1
fi
if [ ${parentPath} = / ]; then
fullPath=${fullPath:-${parentPath}${base}}
fi
fullPath=${fullPath:-${parentPath}/${base}}
if [ ! -e ${fullPath} ]; then
echo "Error: does not exist" >&2
exit 1
fi
echo ${fullPath}
This works with both Linux and Mac OSX:
echo $(pwd)$/$(ls file.txt)
find / -samefile file.txt -print
Will find all the links to the file with the same inode number as file.txt
adding a -xdev flag will avoid find to cross device boundaries ("mount points"). (But this will probably cause nothing to be found if the find does not start at a directory on the same device as file.txt)
Do note that find can report multiple paths for a single filesystem object, because an Inode can be linked by more than one directory entry, possibly even using different names. For instance:
find /bin -samefile /bin/gunzip -ls
Will output:
12845178 4 -rwxr-xr-x 2 root root 2251 feb 9 2012 /bin/uncompress
12845178 4 -rwxr-xr-x 2 root root 2251 feb 9 2012 /bin/gunzip
Usually:
find `pwd` | grep <filename>
Alternatively, just for the current folder:
find `pwd` -maxdepth 1 | grep <filename>
This will work for both file and folder:
getAbsolutePath(){
[[ -d $1 ]] && { cd "$1"; echo "$(pwd -P)"; } ||
{ cd "$(dirname "$1")" || exit 1; echo "$(pwd -P)/$(basename "$1")"; }
}
Another Linux utility, that does this job:
fname <file>
For Mac OS, if you just want to get the path of a file in the finder, control click the file, and scroll down to "Services" at the bottom. You get many choices, including "copy path" and "copy full path". Clicking on one of these puts the path on the clipboard.
fp () {
PHYS_DIR=`pwd -P`
RESULT=$PHYS_DIR/$1
echo $RESULT | pbcopy
echo $RESULT
}
Copies the text to your clipboard and displays the text on the terminal window.
:)
(I copied some of the code from another stack overflow answer but cannot find that answer anymore)
In Mac OSX, do the following steps:
cd into the directory of the target file.
Type either of the following terminal commands.
Terminal
ls "`pwd`/file.txt"
echo $(pwd)/file.txt
Replace file.txt with your actual file name.
Press Enter
you#you:~/test$ ls
file
you#you:~/test$ path="`pwd`/`ls`"
you#you:~/test$ echo $path
/home/you/test/file
Beside "readlink -f" , another commonly used command:
$find /the/long/path/but/I/can/use/TAB/to/auto/it/to/ -name myfile
/the/long/path/but/I/can/use/TAB/to/auto/it/to/myfile
$
This also give the full path and file name at console
Off-topic: This method just gives relative links, not absolute. The readlink -f command is the right one.

bash script runs from shell but not from cron job

Cron installation is vixie-cron
/etc/cron.daily/rmspam.cron
#!/bin/bash
/usr/bin/rm /home/user/Maildir/.SPAM/cur/*;
I Have this simple bash script that I want to add to a cron job (also includes spam learning commands before) but this part always fails with "File or directory not found" From what I figure is the metachar isn't being interperted correctly when run as a cron job. If I execute the script from the commandline it works fine.
I'd like a why for this not working and of course a working solution :)
Thanks
edit #1
came back to this question when I got popular question badge for it. I first did this,
#!/bin/bash
find /home/user/Maildir/.SPAM/cur/ -t file | xargs rm
and just recently was reading through the xargs man page and changed it to this
#!/bin/bash
find /home/user/Maildir/.SPAM/cur/ -t file | xargs --no-run-if-empty rm
short xargs option is -r
If there are no files in the directory, then the wildcard will not be expanded and will be passed to the command directly. There is no file called "*", and then the command fails with "File or directory not found." Try this instead:
if [ -f /home/user/Maildir/.SPAM/cur/* ]; then
rm /home/user/Maildir/.SPAM/cur/*
fi
Or just use the "-f" flag to rm. The other problem with this command is what happens when there is too much spam for the maximum length of the command line. Something like this is probably better overall:
find /home/user/Maildir/.SPAM/cur -type f -exec rm '{}' +
If you have an old find that only execs rm one file at a time:
find /home/user/Maildir/.SPAM/cur -type f | xargs rm
That handles too many files as well as no files. Thanks to Charles Duffy for pointing out the + option to -exec in find.
Are you specifying the full path to the script in the cronjob?
00 3 * * * /home/me/myscript.sh
rather than
00 3 * * * myscript.sh
On another note, it's /bin/rm on all of the linux boxes I have access to. Have you double-checked that it really is /usr/bin/rm on your machine?
try adding
MAILTO=your#email.address
to the top of your cron file and you should get any input/errors mailed to you.
Also consider adding the command as a cronjob
0 30 * * * /usr/bin/rm /home/user/Maildir/.SPAM/cur/*
Try using a force option and forget about adding a path to rm command. I think it should not be needed...
rm -f
This will ensure that even if there are no files in the directory, rm command will not fail. If this is a part of a shell script, the * should work. It looks to me that you might have an empty dir...
I understand that the rest of the script is being executed, right?
Is rm really located in /usr/bin/ on your system? I have always thought that rm should reside in /bin/.

Resources