I have scrapped a perl snippet off the web for use in my bash scrip and for reasons too long to go into, it will be better if I could achieve what it tries to do directly in bash.
Here is the script:
bash stuff
...
perl <<'EOF'
use 5.006;
use strict;
use warnings;
if (! can_run("ldconfig")) {
die "you need to have ldconfig in your PATH env to proceed.\n";
}
# check if we can run some command
sub can_run {
my ($cmd) = #_;
#warn "can run: #_\n";
my $_cmd = $cmd;
return $_cmd if -x $_cmd;
return undef;
EOF
more bash stuff
Basically, the question could be rephrased as , "how can I check if ldconfig is in the PATH env using bash?"
You want bash's builtin type command:
if type -P ldconfig; then
echo "ldconfig is in the PATH"
else
echo "ldconfig is not in the PATH"
fi
Expressed negatively:
if ! type -P ldconfig; then
echo "ldconfig is not in the PATH"
fi
A more straightforward solution would be to invoke the shell and the which command:
$path = `which ldconfig`;
if ($path) {
...
}
If ldconfig is recognised, the path to its executable will be returned, empty output otherwise.
Or, if this Perl script is not going to do anything more than that, you can dismiss it and execute the same command from bash.
I refined #glenn jackman's answer to make it "quiet". It worked as it was but it output "/sbin/ldconfig" to the screen in addition to the echo when in the path. With this modification, only the echo is output:
type ldconfig &>/dev/null
if [ "$?" -eq 0 ]
then
echo "ldconfig is in the PATH"
else
echo "ldconfig is not in the PATH"
fi
Thanks to all.
Related
Is there an equivalent of shell's "pwd -L" in perl?
I want the current working directory with symlinks unresolved?
My current working directory is "/path1/dir1/dir2/dir3", and here dir1 is symlink to test1/test2. I want current working directory to be "/path1/dir1/dir2/dir3" via perl script. What I am getting is /path1/test1/test2/dir2/dir3.
How can I get the current working directory to be the path with no symlinks resolved? In other words, I would want to implement shell's pwd -L.
use the perl backtick operator to run the pwd -L command on your system and capture the output into a variable, this works on my system:
perl -e 'chomp( my $pwdl = `pwd -L` ); print "$pwdl\n";'
An attempt to replicate the behavior of bash's pwd builtin using just perl (In particular, with the aid of the Path::Tiny and core Cwd modules):
First, from help pwd in a bash shell:
-L print the value of $PWD if it names the current working directory
-P print the physical directory, without any symbolic links
(The GNU coreutils version of pwd(1) also reads the PWD environment variable for its implementation of -L, which is why running it with qx// works even though it doesn't have access to the shell's internal variables keeping track of the working directory and path taken to it)
$ pwd -P # First, play with absolute path with symlinks resolved
/.../test1/test2/dir2/dir3
$ perl -MCwd -E 'say getcwd'
/.../test1/test2/dir2/dir3
$ perl -MPath::Tiny -E 'say Path::Tiny->cwd'
/.../test1/test2/dir2/dir3
$ pwd -L # Using $PWD to preserve the symlinks
/.../dir1/dir2/dir3
$ /bin/pwd -L
/.../dir1/dir2/dir3
$ PWD=/foo/bar /bin/pwd -L # Try to fake it out
/.../test1/test2/dir2/dir3
$ perl -MPath::Tiny -E 'my $pwd = path($ENV{PWD}); say $pwd if $pwd->realpath eq Path::Tiny->cwd'
/.../dir1/dir2/dir3
As a function (With some added checks so it can handle a missing $PWD environment var or one that points to a non-existent path):
#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
use Path::Tiny;
sub is_same_file ($$) {
my $s1 = $_[0]->stat;
my $s2 = $_[1]->stat;
return $s1->dev == $s2->dev && $s1->ino == $s2->ino;
}
sub get_working_dir () {
my $cwd = Path::Tiny->cwd;
# $ENV{PWD} must exist and be non-empty
if (exists $ENV{PWD} && $ENV{PWD} ne "") {
my $pwd = path($ENV{PWD});
# And must point to a directory that is the same filesystem entity as cwd
return $pwd->is_dir && is_same_file($pwd, $cwd) ? $pwd : $cwd;
} else {
return $cwd;
}
}
say get_working_dir;
I need to run a system command which would go to a directory and delete sub directories excluding files if present. I wrote the below command to perform this operation:
system("cd /home/faizan/test/cache ; for i in *\; do if [ -d \"$i\" ]\; then echo \$i fi done");
The command above keeps throwing syntax error. I have tried multiple combinations but still not clear how this should go. Please suggest.
Well, your command line does contain syntax errors. Try this:
system("cd /home/faizan/test/cache ; for i in *; do if [ -d \"\$i\" ]; then echo \$i; fi; done");
Or better yet, only loop over directories in the first place;
system("for i in /home/faizan/test/cache/*/.; do echo \$i; done");
Or better yet, do it without a loop:
system("echo /home/faizan/test/cache/*/.");
(I suppose you will want to rmdir instead of echo once it is properly debugged.)
Or better yet, do it all in Perl. There is nothing here which requires system().
You're still best off trying this as a bash command first. Formatting that properly makes it much clearer that you're missing statement terminators:
for i in *; do
if [ -d "$i" ]; then
echo $i
fi
done
And condensing that by replacing new lines with semicolons (apart from after do/then):
for i in *; do if [ -d "$i" ]; then echo $i; fi; done
Or as has been mentioned, just do it in Perl (I haven't tested this to the point of actually uncommenting remove_tree - be careful!):
use strict;
use warnings;
use File::Path 'remove_tree';
use feature 'say';
chdir '/tmp';
opendir my $cache, '.';
while (my $item = readdir($cache)) {
if ($item !~ /^\.\.?$/ && -d $item) {
say "Deleting '$item'...";
# remove_tree($item);
}
}
Using system
my #args = ("cd /home/faizan/test/cache ; for i in *; do if [ -d \"\$i\" ]; then echo \$i; fi; done");
system(#args);
Using Subroutine
sub do_stuff {
my #args = ( "bash", "-c", shift );
system(#args);
}
do_stuff("cd /home/faizan/test/cache ; for i in *; do if [ -d \"\$i\" ]; then echo \$i; fi; done");
As question title stand for system command, this will answer directly, but the sample command using bash contain only thing that will be simplier in perl only (take a look at other answer using opendir and -d in perl).
If you want to use system (instead of open $cmdHandle,"bash -c ... |"), the prefered syntax for execution commands like system or exec, is to let perl parsing the command line.
Try this (as you've already done):
perl -e 'system("bash -c \"echo hello world\"")'
hello world
perl -e 'system "bash -c \"echo hello world\"";'
hello world
And now better, same but letting perl ensure command line parsing, try this:
perl -e 'system "bash","-c","echo hello world";'
hello world
There are clearly 3 argument of system command:
bash
-c
the script
or little more:
perl -e 'system "bash","-c","echo hello world;date +\"Now it is %T\";";'
hello world
Now it is 11:43:44
as you can see in last purpose, there is no double double-quotes enclosing bash script part of command line.
**Nota: on command line, using perl -e '...' or perl -e "...", it's a little heavy to play with quotes and double-quotes. In a script, you could mix them:
system 'bash','-c','for ((i=10;i--;));do printf "Number: %2d\n" $i;done';
or even:
system 'bash','-c','for ((i=10;i--;));do'."\n".
'printf "Number: %2d\n" $i'."\n".
'done';
Using dots . for concatening part of (script part) string, there are always 3 arguments.
If I have a basic .sh file containing the following script code:
#!/bin/sh
rm -rf "MyFolder"
How do I make this running script file display results to the terminal that will indicate if the directory removal was successful?
You don't really need to make it say it was successful. You could have it say something only on error ✖, and then silence means success ✔.
That's how the Unix philosophy works:
The rule of silence, also referred to as the silence is golden rule, is an important part of the Unix philosophy that states that when a program has nothing surprising, interesting or useful to say, it should say nothing. It means that well-behaved programs should treat their users' attention and concentration as being valuable and thus perform their tasks as unobtrusively as possible. That is, silence in itself is a virtue. http://www.linfo.org/rule_of_silence.html
That's the way rm itself behaves.
If you are asking about the general case, as suggested by your question's title, you can run your script with sh -x scriptname to see what it's doing. It's also quite common to write diagnostic output into the script itself, and control it with an option.
#!/bin/sh
verbose=false
case $1 in -v | --verbose )
verbose=true
shift ;;
esac
say () {
$verbose || return
echo "$0: $#" >&2
}
say "Removing $dir ..."
rm -rf "$dir" || say "Failed."
If you run this script without any options, it will run silently, like a well-behaved Unix utility should. If you run it with the -v option, it will print some diagnostics to standard error.
rm -rf "My Folder" && echo "Done" || echo "Error!"
You can read more on creating a sequence of pipelines in bash manual
In the bash (and other similar shells) the ? environment variable gives you the exit code of the last executed command. So you can do:
#!/bin/sh
rm -rf "My Folder"
echo $?
UPDATE
If once the rm command has been executed the directory doesn't exist (because it has been successfully removed or because it didn't exist when the command was executed) the script will print 0. If the directory exists (which will mean that the command has been unable to remove it) then the script will print an exit code other than 0. If I understand properly the question this is exactly the requested behavior. If it is not, please correct me.
The previous answers was wrong : rm don't exit with error code > 0 when the dir isn't present.
Instead, I recommend to use :
dir='/path/to/dir'
if [[ -d $dir ]]; then
rm -rf "$dir"
fi
If you want rm to return a status, remove -f flag.
Example on Linux Mint (the dir doesn't exists):
$ rm -rf /tmp/sdfghjklm
$ echo $?
0
$ rm -r /tmp/sdfghjklm
$ echo $?
1
I want to call my perl script as a command from command line.
Example lets say I have a perl file like following
#!/usr/local/bin/perl -w
#args =("mvn","package");
system(#args) == 0
or die "system #args failed"
I right not call this using package.pl
I tried doing the following
#!/bin/sh
eval 'exec /bin/perl –x -S $0 ${1+"$#"}'
if 0;
#!/usr/local/bin/perl -w
#args =("mvn","package");
system(#args) == 0
or die "system #args failed"
and then name the file 'package' .Do chmod on package
When I try to run package, then I get the error
"Can't open perl script []x:No such file or directory
Can someone please point me out , as to how to do this properly??
Thanks
Neeraj
Change to the name you want to use and make it executable:
cp package.pl package
chmod +x package
Run it:
package
or:
./package
Changed single quotes to double quotes and escaped inner double quotes. Also, there seems to be some problem with paths. Try calling your script with absolute path. I tried adding "./" and it worked:
#!/bin/sh
echo "This is shell"
eval "exec /usr/bin/perl -x -S ./$0 ${1+\"$#\"}"
if 0;
#!/usr/bin/perl -w
print "This is Perl\n";
I'm not sure if these paths are duplicates. Given the relative path, how do I determine absolute path using a shell script?
Example:
relative path: /x/y/../../a/b/z/../c/d
absolute path: /a/b/c/d
The most reliable method I've come across in unix is readlink -f:
$ readlink -f /x/y/../../a/b/z/../c/d
/a/b/c/d
A couple caveats:
This also has the side-effect of resolving all symlinks. This may or may not be desirable, but usually is.
readlink will give a blank result if you reference a non-existant directory. If you want to support non-existant paths, use readlink -m instead. Unfortunately this option doesn't exist on versions of readlink released before ~2005.
From this source comes:
#!/bin/bash
# Assume parameter passed in is a relative path to a directory.
# For brevity, we won't do argument type or length checking.
ABS_PATH=`cd "$1"; pwd` # double quotes for paths that contain spaces etc...
echo "Absolute path: $ABS_PATH"
You can also do a Perl one-liner, e.g. using Cwd::abs_path
Using bash
# Directory
relative_dir="folder/subfolder/"
absolute_dir="$( cd "$relative_dir" && pwd )"
# File
relative_file="folder/subfolder/file"
absolute_file="$( cd "${relative_file%/*}" && pwd )"/"${relative_file##*/}"
${relative_file%/*} is same result as dirname "$relative_file"
${relative_file##*/} is same result as basename "$relative_file"
Caveats: Does not resolve symbolic links (i.e. does not canonicalize path ) => May not differentiate all duplicates if you use symbolic links.
Using realpath
Command realpath does the job. An alternative is to use readlink -e (or readlink -f). However realpath is not often installed by default. If you cannot be sure realpath or readlink is present, you can substitute it using perl (see below).
Using perl
Steven Kramer proposes a shell alias if realpath is not available in your system:
$ alias realpath="perl -MCwd -e 'print Cwd::realpath(\$ARGV[0]),qq<\n>'"
$ realpath path/folder/file
/home/user/absolute/path/folder/file
or if you prefer using directly perl:
$ perl -MCwd -e 'print Cwd::realpath($ARGV[0]),qq<\n>' path/folder/file
/home/user/absolute/path/folder/file
This one-line perl command uses Cwd::realpath. There are in fact three perl functions. They take a single argument and return the absolute pathname. Below details are from documentation Perl5 > Core modules > Cwd.
abs_path() uses the same algorithm as getcwd(). Symbolic links and relative-path components (. and ..) are resolved to return the canonical pathname, just like realpath.
use Cwd 'abs_path';
my $abs_path = abs_path($file);
realpath() is a synonym for abs_path()
use Cwd 'realpath';
my $abs_path = realpath($file);
fast_abs_path() is a more dangerous, but potentially faster version of abs_path()
use Cwd 'fast_abs_path';
my $abs_path = fast_abs_path($file);
These functions are exported only on request => therefore use Cwd to avoid the "Undefined subroutine" error as pointed out by arielf. If you want to import all these three functions, you can use a single use Cwd line:
use Cwd qw(abs_path realpath fast_abs_path);
Take a look at 'realpath'.
$ realpath
usage: realpath [-q] path [...]
$ realpath ../../../../../
/data/home
Since I've run into this many times over the years, and this time around I needed a pure bash portable version that I could use on OSX and linux, I went ahead and wrote one:
The living version lives here:
https://github.com/keen99/shell-functions/tree/master/resolve_path
but for the sake of SO, here's the current version (I feel it's well tested..but I'm open to feedback!)
Might not be difficult to make it work for plain bourne shell (sh), but I didn't try...I like $FUNCNAME too much. :)
#!/bin/bash
resolve_path() {
#I'm bash only, please!
# usage: resolve_path <a file or directory>
# follows symlinks and relative paths, returns a full real path
#
local owd="$PWD"
#echo "$FUNCNAME for $1" >&2
local opath="$1"
local npath=""
local obase=$(basename "$opath")
local odir=$(dirname "$opath")
if [[ -L "$opath" ]]
then
#it's a link.
#file or directory, we want to cd into it's dir
cd $odir
#then extract where the link points.
npath=$(readlink "$obase")
#have to -L BEFORE we -f, because -f includes -L :(
if [[ -L $npath ]]
then
#the link points to another symlink, so go follow that.
resolve_path "$npath"
#and finish out early, we're done.
return $?
#done
elif [[ -f $npath ]]
#the link points to a file.
then
#get the dir for the new file
nbase=$(basename $npath)
npath=$(dirname $npath)
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -d $npath ]]
then
#the link points to a directory.
cd "$npath"
ndir=$(pwd -P)
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
echo "opath [[ $opath ]]" >&2
echo "npath [[ $npath ]]" >&2
return 1
fi
else
if ! [[ -e "$opath" ]]
then
echo "$FUNCNAME: $opath: No such file or directory" >&2
return 1
#and break early
elif [[ -d "$opath" ]]
then
cd "$opath"
ndir=$(pwd -P)
retval=0
#done
elif [[ -f "$opath" ]]
then
cd $odir
ndir=$(pwd -P)
nbase=$(basename "$opath")
retval=0
#done
else
echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
echo "opath [[ $opath ]]" >&2
return 1
fi
fi
#now assemble our output
echo -n "$ndir"
if [[ "x${nbase:=}" != "x" ]]
then
echo "/$nbase"
else
echo
fi
#now return to where we were
cd "$owd"
return $retval
}
here's a classic example, thanks to brew:
%% ls -l `which mvn`
lrwxr-xr-x 1 draistrick 502 29 Dec 17 10:50 /usr/local/bin/mvn# -> ../Cellar/maven/3.2.3/bin/mvn
use this function and it will return the -real- path:
%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`
%% test.sh
relative symlinked path:
/usr/local/bin/mvn
and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn
I wanted to use realpath but it is not available on my system (macOS), so I came up with this script:
#!/bin/sh
# NAME
# absolute_path.sh -- convert relative path into absolute path
#
# SYNOPSYS
# absolute_path.sh ../relative/path/to/file
echo "$(cd $(dirname $1); pwd)/$(basename $1)"
Example:
./absolute_path.sh ../styles/academy-of-management-review.csl
/Users/doej/GitHub/styles/academy-of-management-review.csl
May be this helps:
$path = "~user/dir/../file"
$resolvedPath = glob($path); # (To resolve paths with '~')
# Since glob does not resolve relative path, we use abs_path
$absPath = abs_path($path);