Linux difference between when to use parentheses - linux

Why do I get extra empty line when running 2). To me 1 is like 2. So why the extra line in 2)?
1)
export p1=$(cd $(dirname $0) && pwd)
# ^
echo p1
2)
export p2=$(cd $(dirname $0)) && pwd
# ^
echo p2

$echo $0
/bin/bash
$ echo $(cd $(dirname $0) && pwd)
/bin
$ echo $(cd $(dirname $0)) && pwd
/home/user
$
In the 1st expression it becomes echo $(cd /bin && pwd). Therefore the inner 2 commands execute in a subshell and return back the pwd value which is then echoed.
In the 2nd expression it gets reduced to echo $(cd /bin) && pwd. Therefore only the cd command executes in a subshell and returns nothing to echo (hence by default echo just prints an empty line). Since echo ran successfully(exit code=0) && results in true and pwd cmd is run in current shell and pwd gets printed

p1 captures the output of cd (empty) and pwd.
p2 only captures the output of cd, and then runs pwd without redirection.
echo p1 prints a literal p1 (with a newline). I guess you didn't actually copy-paste from your terminal, but instead typed in some thing else.
peter#tesla:~$ export p2=$(true) && pwd
/home/peter
peter#tesla:~$ echo "x${p2}x"
xx
cd in a subshell doesn't affect the parent shell's pwd, so I just substituted the true command to make it more readable.

Related

Up a Directory Script?

I've been trying to create a bash script that can move up a directory. I created this script, but when ran it does not execute anything. I tried adding a print statement to it, and that does work. Is there a certain way I should be executing this?
Script:
#!/usr/bin/zsh
DIR=$1
NUM=$PWD
for ((c=1; c <= DIR; c++))
do
echo $NUM
cd $NUM/..
done
If I understand, you like to move a directory up in the directory tree.
This is a script make aliases in .bashrc Like: alias up1='cd../' alias up2='cd../../' and so on, I limit the depth 9. If you Run it multiple times the script only crate the not existing entrys. May I will able to make it delete entry if you give less depth then script would manage this alias.
Not exactly you looking for but since script running in they own instance you cannot make them affect your current shell. Also after this script create aliases you need re authenticate or open a new shell, from that point they will work till you not delete the alias entry from .bashrc.
#!/bin/sh
[[ ! $# == 1 ]] && echo "Only one parameter accepted" && exit 1
[[ $( echo $1 | grep -c ^[1-9]$ ) -eq 0 ]] && echo "parameter must be between 1 and 9" && exit 1
cdcommand=""
for (( i = 1 ; i <= $1 ; i++ )); do
cdcommand=$(echo $cdcommand | sed 's/^/\.\.\//g')
[[ $( cat ~/.bashrc | grep -c "alias up$i='cd $cdcommand' ") == 0 ]] &&
echo "alias up$i='cd $cdcommand' " >> ~/.bashrc
done

Set shell script Variable to output of command

Im trying to cd into the md5 hash of whatever variable is set into the script but I do not get the correct value of md5, I think it has something to do with how I'm declaring my variables. Thank you for any help!
#!/bin/bash
var1=$1
md5=$(-n $var1 | md5sum)
cd /var/www/html/$md5
I expected it to take me to a directory given by the md5 hash:
$ ./myscript hello
(no output)
$ pwd
/var/www/html/5d41402abc4b2a76b9719d911017c592
Instead, it gives me errors and tries to cd to the wrong path:
$ ./myscript hello
./myscript: line 3: -n: command not found
./myscript: line 4: cd: /var/www/html/d41d8cd98f00b204e9800998ecf8427e: No such file or directory
$ pwd
/home/me
The md5sum it incorrectly tries to cd to is also the same no matter which value I input.
This works as a solution for anyone else having this issue
#!/bin/bash
md5=$*
hash="$(echo -n "$md5" | md5sum )"
cd /var/www/html/$hash
Your script:
#!/bin/bash
var1=$1
md5=$(-n $var1 | md5sum)
cd /var/www/html/$md5
This has a few issues:
-n is not a valid command in the pipeline -n $var1 | md5sum.
md5sum returns more than just the MD5 digest.
Changing the directory in a script will not be reflected in the calling shell.
Input is used unquoted.
I would write a shell function for this, rather than a script:
function md5cd {
dir="$( printf "%s" "$1" | md5sum - | cut -d ' ' -f 1 )"
cd /var/www/html/"$dir" || return 1
}
The function computes the MD5 digest of the given string using md5sum and cuts off the filename (-) that's part of the output. It then changes directory to the specified location. If the target directory does not exist, it signals this by returning a non-zero exit status.
Extending it to cd to a path constructed from the path on the command line, but with the last path element changed into a MD5 digest (just for fun):
function md5cd {
word="${1##*/}"
if [[ "$word" == "$1" ]]; then
prefix="."
else
prefix="${1%/*}"
fi
dir="$( cut -d ' ' -f 1 <( printf "%s" "$word" | md5sum - ) )"
cd "$prefix"/"$dir" || return 1
}
Testing it:
$ pwd
/home/myself
$ echo -n "hex this" | md5sum
990c0fc93296f9eed6651729c1c726d4 -
$ mkdir /tmp/990c0fc93296f9eed6651729c1c726d4
$ md5cd /tmp/"hex this"
$ pwd
/tmp/990c0fc93296f9eed6651729c1c726d4

Explain this shell script

Autoconf documentation recommends this snippet for portability:
# Create a temporary directory $tmp in $TMPDIR (default /tmp).
# Use mktemp if possible; otherwise fall back on mkdir,
# with $RANDOM to make collisions less likely.
: ${TMPDIR=/tmp}
{
tmp=`
(umask 077 && mktemp -d "$TMPDIR/fooXXXXXX") 2>/dev/null
` &&
test -n "$tmp" && test -d "$tmp"
} || {
tmp=$TMPDIR/foo$$-$RANDOM
(umask 077 && mkdir "$tmp")
} || exit $?
I don’t understand the first line:
Why does it begin with a :?
Why is the TMPDIR variable used just after that?
: ${TMPDIR=/tmp} means:
: is null command in bash , but shell assign /tmp to TMPDIR variable if content of TMPDIR is null
The ": ${TMPDIR=/tmp}" line is invoking ":" shell built-in, which does nothing, with a single argument - result of "${TMPDIR=/tmp}" parameter expansion.
This parameter expansion assigns "/tmp" to TMPDIR variable, if it was unset (not set to anything before, even empty string), and then expands to the value of TMPDIR, which is ignored by ":" builtin.

about the Linux Shell 'While' command

code:
path=$PATH:
while [ -n $path ]
do
ls -ld ${path%%:*}
path=${path#*:}
done
I want to get the each part of path .When run the script ,it can not get out of the while process 。Please tell me why . Is some problem in 'while [ -n $path ]' ?
The final cut never results in an empty string. If you have a:b:c, you'll strip off the a and then the b, but never the c. I.e., this:
${path#*:}
Will always result in a non-empty string for the last piece of the path. Since the -n check looks for an empty string, your loop runs forever.
If $path doesn't have a colon in it, ${path#*:} will return $path. So you have an infinite loop.
p="foo"
$ echo ${p#*:}
foo
$ p="foo:bar"
$ echo ${p#*:}
bar
You have some bugs in your code. This should do the trick:
path=$PATH
while [[ $path != '' ]]; do
# you can replace echo to whatever you need, like ls -ld
echo ${path%%:*}
if echo $path | grep ':' >/dev/null; then
path=${path#*:}
else path=''
fi
done
Your path, after is initialized, will always check True for [ -n path ] test. This is the main reason for which you never get out of the while loop.

Bash create variable then assign value to it

For this problem I have two values, curdir and curlevel, which change throughout my script. I want to know if it's possible to create a variable and then use that value as the name for another value. For example
temp="dir_${curdir}_${curlevel}"
$temp=$name_of_directory **<----Is there a legitimate way to do this?**
so if initially curdir=1 and curlevel=0 then
$(temp)=directory_one
is equal to
dir_1_0=directory_one
then later if curdir=2 and curlevel=4, I can reset temp and then have
$(temp)=another_directory
is the same as
dir_2_4=another_directory
so I could make a call such as
cd $(temp)
which will move me to different directories when I need to
I think what you want is to use eval. Like so:
$ foo=bar
$ bar=baz
$ eval qux=\$$foo
$ echo $qux
baz
So what you could do is something like
eval temp=\$$"dir_${curdir}_${curlevel}"
cd $temp
The trick for this is to use eval - several times.
curdir=1
curlevel=0
temp='dir_${curdir}_${curlevel}' # Note single quotes!
x=$(eval echo $temp)
eval $x=$PWD
cd /tmp
curdir=2
curlevel=4
x=$(eval echo $temp)
eval $x=$PWD
echo $dir_1_0
echo $dir_2_4
The output of sh -x script:
+ curdir=1
+ curlevel=0
+ temp='dir_${curdir}_${curlevel}'
++ eval echo 'dir_${curdir}_${curlevel}'
+++ echo dir_1_0
+ x=dir_1_0
+ eval dir_1_0=/Users/jleffler/tmp/soq
++ dir_1_0=/Users/jleffler/tmp/soq
+ cd /tmp
+ curdir=2
+ curlevel=4
++ eval echo 'dir_${curdir}_${curlevel}'
+++ echo dir_2_4
+ x=dir_2_4
+ eval dir_2_4=/tmp
++ dir_2_4=/tmp
+ echo /Users/jleffler/tmp/soq
/Users/jleffler/tmp/soq
+ echo /tmp
/tmp
The output of sh script:
/Users/jleffler/tmp/soq
/tmp
Converted to a function:
change_dir()
{
temp='dir_${curdir}_${curlevel}' # Note single quotes!
x=$(eval echo $temp)
eval $x=$PWD
cd $1
}
curdir=1
curlevel=0
change_dir /tmp
curdir=2
curlevel=4
change_dir $HOME
echo $dir_1_0
echo $dir_2_4
pwd
Output:
/Users/jleffler/tmp/soq
/tmp
/Users/jleffler
The recorded names are the names of the directory being left, not the one you arrive at.
The secure way to do this is to use indirection, associative arrays (Bash 4), functions or declare:
Use declare:
declare $temp=$name_of_directory
Use indirection:
bar=42
foo=bar
echo ${!foo}
IFS= read -r $foo <<< 101
echo ${!foo}
Please take note of the security implications of eval.

Resources