how to pass a variable through read in bash - bash

I'm trying to make a script which asks for a directory path and then creates that directory. I'd like to be able to pass variables to the read builtin so pathnames so I don't have to type out full paths:
function make_dir {
echo "What is the path of the directory to be created?"
read directory
mkdir "$directory"
}
so I'd type:
make_dir
What is the path of the directory to be created?
$HOME/temp_dir
mkdir: cannot create directory `$HOME/temp_dir': No such file or directory
So I'd like to have $HOME expanded into /home/user/ and the script to make the directory /home/user/temp_dir, but I can't seem to get the expansion to work.
If I modify the make_dir function to show_dir below
function show_dir {
echo "What is the path of the directory to be created?"
read directory
echo "The directory is $directory"
}
and I type $HOME/temp_dir and get the following:
make_dir
What is the path of the directory to be created?
$HOME/temp_dir
The directory is $HOME/temp_dir
with no expansion. Any ideas on how to get this to work?

It's a little cumbersome, but one option is to use the -e flag to tell read to use Readline to get the input, then use Readline to expand the line after typing it, but before hitting Enter.
$ read -e directory
$HOME/dir
Now type Meta-Control-e, and Readline will expand the input just as if it were being processed prior to execution as a shell command. (Note that the Meta key is probably Alt or Esc, depending on your terminal setup.)

You are actually making things more difficult by attempting to get the directory with read. Unless you have an absolute requirement to use read, you are better off passing the directory to your function as an argument. For example:
function make_dir {
[ -n "$1" ] || {
printf "\n usage: make_dir <path_to_create>\n\n"
return 1
}
mkdir -p "$1" || {
printf "\n error: unable to create '$1', check permissions\n\n"
}
}
example:
$ function make_dir {
> [ -n "$1" ] || {
> printf "\n usage: make_dir <path_to_create>\n\n"
> return 1
> }
> mkdir -p "$1" || {
> printf "\n error: unable to create '$1', check permissions\n\n"
> }
> }
$ make_dir $HOME/temp_dir
$ ls -al temp_dir
total 8
drwxr-xr-x 2 david david 4096 Nov 26 15:34 .
drwxr-xr-x 76 david david 4096 Nov 26 15:34 ..
$ make_dir
usage: make_dir <path_to_create>
When you pass the directory to your function as an argument instead of using read, you can easily adjust your function to take/create multiple directories as well:
function make_dir {
[ -n "$1" ] || {
printf "\n usage: make_dir <path_to_create> [path2, ..]\n\n"
return 1
}
for i in "$#" ; do
mkdir -p "$i" || {
printf "\n error: unable to create '$i', check permissions\n\n"
}
done
}
example:
$ make_dir temp_dir_{1..3}
$ ls -1d temp_*
temp_dir_1
temp_dir_2
temp_dir_3

change the following line:
mkdir "$directory"
with
eval mkdir -p "$directory"

Related

how to call a command from a bash script in Linux

I am trying to call a command from a script (defined as functions inside .bashrc) as following
the first function hooks the rm command by deleting any file except a file named "forensicfile1"
#hooking rm command
function rm(){
if [[ $1 = "forensicfile1" ]]; then
echo "$1 file is deleted by user ">forensicaudit
else
rm $1
fi
}
the second hooking function is trying to hook the ls command by listing all files except the forensicfile1
#hooking ls command
function ls(){
if [[ $PWD = "/home/waleed/forensicdata" ]]
then
alias myls='ls "/home/waleed/forensicdata" -1 "forensicfile1" '
myls
else
ls $PWD
fi
}
however the following lines are not working
rm $1 // within the first function
alias myls='ls "/home/waleed/forensicdata" -1 "forensicfile1" '
myls
ls $PWD
can anyone help?

Maximum nested function level reached

I have directory hierarchy where the "root" directory has a file called "text.txt". I want to find this "root" directory and then run the command 'foo' from within it. Here is what I currently have
# Locates the root directory
locateRoot () {
local root
#Findup looks up through the directory hierarchy. I'm sure this works.
root=$(findup text.txt 2>&/dev/null)
if [[ $? -ne 0 ]]
then
echo "Root not found"
return 1
fi
echo root
}
# Does foo from within the root directory
runFoo () {
local myDir
myDir=$(locateRoot)
pushd $myDir 1>&/dev/null
foo $#
popd 1>&/dev/null
}
However, whenever I run this program I get:
maximum nested function level reached
What's wrong with what I have? I'm positive that foo works as expected.
in you locateRoot function you just echo only root not content of it, which is wrong and your script seems to very lengthy to perform some simple task.i give you sample script which print path to directory which contain text.txt file.
#! /bin/bash
locateRoot ()
{
var=$(find / -iname "text.txt" -printf '%h\n' | sort -u)
printf "%s \n" "$var"
}
you can see absolute path to that directory which contain that file. You can modify above script to perform certain task as you want by just cd to that directory like
cd $var
//execute your command in $var directory

How to tell if a filename is a directory, not a file

I need to check if a parameter passed to a bash script is a folder or a file. It may or may not end with /
xpath=$(dirname "$1")
strips out the dirname if there was no trailing /
Thanks
Given the file a and dir t.
You can use the command file:
$ file t
t: directory
$ file a
a: ASCII text
Or also the -f (file) and -d (dir) flags.
$ [ -f a ] && echo "this is a file"
this is a file
$ [ -f t ] && echo "this is a file"
$
$ [ -d t ] && echo "this is a dir"
this is a dir
$ [ -d a ] && echo "this is a dir"
$
use "test -f" or "test -d" with the path. Though "dirname" always returns name of directory, never a filename, however it may return a directory in which file resides, or a directory in which directory resides, depending if argument is file or directory. "basename" returns filename or directory without preceeding path it resides in.

Generate warning when multiple executables in path when executing command in bash

Say I have:
>which -a foo
/bin/foo
/usr/bin/foo
I want something like:
>foo
Warning: multiple foo in PATH
... foo gets executed ...
This functionality would have saved me really really lots of time today.
I should have guessed this is happening earlier but the problem was unclear
to me at the beggining and I started to dig in quite opposite direction.
Well, you can do it, but it is not so easy as you may think.
First, you need to create a function that will check
all directories in the PATH, and look there for the command you try to run.
Then you need to bind this function to the DEBUG trap of your current shell.
I wrote a small script that does that:
$ cat /tmp/1.sh
check_command()
{
n=0
DIRS=""
for i in $(echo $PATH| tr : " ")
do
if [ -x "$i/$1" ]
then
n=$[n+1]
DIRS="$DIRS $i"
fi
done
if [ "$n" -gt 1 ]
then
echo "Warning: there are multiple commands in different PATH directories: "$DIRS
fi
}
preexec () {
check_command $1
}
preexec_invoke_exec () {
[ -n "$COMP_LINE" ] && return # do nothing if completing
local this_command=`history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g"`;
preexec "$this_command"
}
trap 'preexec_invoke_exec' DEBUG
Example of usage:
$ . /tmp/1.sh
$ sudo cp /bin/date /usr/bin/test_it
$ sudo cp /bin/date /bin/test_it
$ test_it
Warning: there are multiple commands in different PATH directories: /usr/bin /bin
Wed Jul 11 15:14:43 CEST 2012
$
It is possible, though a bit trick to generalise. See my answer to https://unix.stackexchange.com/q/42579/20437 for the history and PROMPT_COMMAND magic. Your checkSanity function would look something like this:
checkSanity() {
cmd="${1%% *}" # strip everything but the first word
candidates=$(which -a $cmd | wc -l)
if (( candidates > 1 )); then
echo "Warning: multiple $cmd in PATH"
fi
}
But that will print the warning after the command finishes, not at the start. Use the DEBUG trap instead to get the wanted result:
trap 'checkSanity "$BASH_COMMAND"' DEBUG

Find file by name up the directory tree, using bash

Using bash, how can I find a file with a specific name somewhere up the directory tree from the pwd?
To be more clear. I want to find a file that sits in my working directory's root, but I don't know where the root is, and my pwd might be anywhere below the root.
Find file.txt up to root
x=`pwd`
while [ "$x" != "/" ] ; do
x=`dirname "$x"`
find "$x" -maxdepth 1 -name file.txt
done
local DIR=$(pwd)
while [ ! -z "$DIR" ] && [ ! -f "$DIR/myFile.txt" ]; do
DIR="${DIR%\/*}"
done
echo $DIR/myFile.txt
I have the following function defined in my ~/.bashrc:
dnif () {
# Recursively list a file from PWD up the directory tree to root
[[ -n $1 ]] || { echo "dnif [ls-opts] name"; return 1; }
local THERE=$PWD RC=2
while [[ $THERE != / ]]
do [[ -e $THERE/${2:-$1} ]] && { ls ${2:+$1} $THERE/${2:-$1}; RC=0; }
THERE=$(dirname $THERE)
done
[[ -e $THERE/${2:-$1} ]] && { ls ${2:+$1} /${2:-$1}; RC=0; }
return $RC
}
which will search for a name you provide as a parameter in each directory upwards from the current to the root, and if found, list it with 'ls' and the optional ls -options that you provide. Example output:
me#host:~/dev/example
$ dnif; echo $?
dnif [ls-opts] name
1
me#host:~/dev/example
$ dnif -alp nonesuch; echo $?
2
me#host:~/dev/example
$ dnif -alp .bashrc; echo $?
-rw-r--r-- 1 me mine 3486 Apr 3 2012 /home/me/.bashrc
0
me#host:~/dev/example
$ dnif -d .
/home/me/dev/example/.
/home/me/dev/.
/home/me/.
/home/.
/.
Please note:
"dnif" is "find" backwards.
The function is a finite loop (not recursive), creates no subshells, and uses Bash built-ins as much as possible for speed.
All hits at each ascending directory level are listed.
The ls -opts are optional, but must precede the required search argument.
The search argument may be a file or directory.
If the search argument is a directory, include the ls -opt '-d' to restrict the results to directory names rather than contents.
The function returns exit code
0 if there is at least one hit,
1 if no parameters are provided for help, and
2 if nothing is found.

Resources