Simulating ENTER key and export another variable together in Linux bash - linux

I should put ENTER key value as a shell input to avoid shell waiting for user input.
echo 'enter'
read FRUIT
case "$FRUIT" in
"apple") echo "Apple pie is quite tasty."
;;
"banana") echo "I like banana nut bread."
;;
"kiwi") echo "New Zealand is famous for kiwi."
;;
esac
export TEST6=TEST
I have learned that 'echo' implicitly has an ENTER value, so tried to run below command to accomplish my requirement.
echo | source ~/.bash_profile
As I expected, I can see the OS prompt instead of seeing Shell wait the user input.
However, I noticed that the last line exporting TEST6 variable doesn't get exported. I mean I can't find the value of the variable TEST6.
Can you let me know what I am missing?

The value is being exported in a subshell run by the pipeline. Use input redirection instead.
source ~/.bash_profile <<< ""
Any string could be used, since you don't appear to care about the actual value used, but an empty string is the equivalent of what echo with no arguments produces. (All here strings implicitly end with a newline.)
If your shell does not support <<<, you can use a POSIX-standard here document instead.
. ~/some_file <<EOF
EOF

Related

What does the linux operator $ do?

I've seen many examples where $USER and similar commands are used but I could never figure out what it meant.
Whenever I search $ on Google, it doesn't recognise the symbol.
it is used to access system variable.
for example:
if you type in linux and/or unix shell
...$ my_var="some value"
...$ echo my_var
will print "some value"
...$ echo $USER
will print the name of shell user
...$ echo $?
will print the result of the previous command when successfully will print 0
in other word the result of "exit (num)" of you shell.
"$" also can indicate you are logged as no-root user for some shells the root will be indicated "#"
The $ is a special character that tells the shell interpreter to interpret the contents following as a value for a variable. It is also called variable substitution.
In the case of commands or command output, it can be used to call a shell command and store it's output as a variable's value. For example:
VAR_1="$(ip link show)"
Calling the variable VAR_1 will print the output of the command ip link show.
This is called command substitution.
You can find out more information on special characters Here
as well as information on wildcards, keywords and more.

Is it possible to make a list of disk in bash?

I'm a beginner and not a native english speaker please excuse my clumsiness.
I'm trying to make a linux install script for personal use (and to learn more about linux and bash scripting) but I'm struggling on finding a way to create a disk selection menu :
I wish to make a list witch would look like that :
NAME SIZE DEVICES
sda 256gib intel-ssdx
sdb 1000gib TLxxxxxxxx
nvme0n1 128gib WDxxxxxxxx
So far i've tried to echo fdisk -l and lsblk in text file and use cat to prompt it
Code :
lsblk
Set DiskLayout=("Automatic Install" "Manual Install" "Check pending change" "Quit")
select DiskLayoutopt in "${DiskLayout[#]}"
do
case $DiskLayoutopt in
"Automatic Install")
read Sdsk -p "Select drive"
;;
"Manual Install")
parted -a optimal
;;
"Check pending change")
echo ""
"Quit")
exit 1
;;
*) echo "invalid option $REPLY";;
esac
done
The following code will get your menu:
#!/usr/bin/env bash
disk=()
size=()
name=()
while IFS= read -r -d $'\0' device; do
device=${device/\/dev\//}
disk+=($device)
name+=("`cat "/sys/class/block/$device/device/model"`")
size+=("`cat "/sys/class/block/$device/size"`")
done < <(find "/dev/" -regex '/dev/sd[a-z]\|/dev/vd[a-z]\|/dev/hd[a-z]' -print0)
for i in `seq 0 $((${#disk[#]}-1))`; do
echo -e "${disk[$i]}\t${name[$i]}\t${size[$i]}"
done
This is some tough bash script... Hope you'll learn quick.
Here's some help:
First line is a shebang to tell your system which interpreter is needed for that script. Indeed, this script only works with bash.
Try running with bash myscript.sh on systems that don't work (ie BSD).
variable=() is an array.
Adding something to that array is done by variable+=("my value")
The while loop reads variable device from what it gets from find command
while read device; do
something
done < <(find)
The find command uses a regular expression that says anything like /dev/sdX where X goes from a to z, or anything like /dev/vdX or anything like /dev/hdX (where X still goes from a to z).
The or operator is a pipe | which has to be escaped with an antislash, hence giving \|.
The devices read by the while look look like '/dev/sda' so we need so strip '/dev/' out of it using the following:
device=${device/\/dev\//}
This is a bash substitution which works the following way:
variable="my foo function"
echo ${variable/foo/bar}
This outputs my bar function.
Indeed, we still need to escape / since this is the separator character for the substition, so it becomes \/.
Getting the disk name via
"`cat "/sys/class/block/$device/device/model"`"
cat "/sys/class/block/sda/device/model" gives the disk model.
In order to get the result into a variable, we'll need to quote it with ` sign, eg:
myvar=`cat /var/file`
Last but not least, the for loop part:
for i in seq 0 $((${#disk[#]}-1)); do
echo -e "${disk[$i]}\t${name[$i]}\t${size[$i]}"
done
${#disk[#]} is the number of elements in array disk.
Actually ${#var} is the number of elements in var, which when being a string, is the number of characters. ${var[#]} means all elements of an array.
seq 0 X returns a sequence of 0 to X numbers, in order to construct the for loop.
Using echo -e translates escaped characters into litterals. In our case '\t' become tabs.
Last but not least, showing ${disk[$i]} is disk array value of index $i where $i is an integer.
Btw, bash is quite limited to do these tasks, but really fun to learn in the first place.
Harder tasks might be better accomplished in a higher level scripting language like Python. Anyway, have fun learning bash, it's a life saver in sysadmin's career.

linux shell - strange 'if'?

I am reading a source code, and find these lines :
if [ -n "${INIT_NAMENODE+1}" ]
then
echo "Initializing namenode"
else
echo "Starting namenode"
fi
how should I interpred the 'if' condition : if [ -n "${INIT_NAMENODE+1}" ] ? ?
The nice thing about this code is that it is not written for a "Linux shell". It is written for the more general category of "UNIX shell". It will work in everything since V7 UNIX (1979) at least. People with lesser portability goals might write it without the -n.
The first item of interest is the ${foo+bar} syntax. This is a test for existence of the foo parameter. If $foo exists, then ${foo+bar} equals bar. If $foo doesn't exist, then ${foo+bar} equals the empty string.
If you look for this in your shell man page, it's usually documented as ${foo:+bar}, along with some other related forms like ${foo:-bar}, and somewhere nearby there's a note explaining that the colon can be omitted from all of them, resulting in slightly different behavior (with the colon, variables whose value is the empty string are treated the same as nonexistent variables).
Next we have the [ -n ... ] test. -n tests the following string for emptiness. It succeeds if the string is non-empty. From the previous paragraph we know that ${INIT_NAMENODE+1} is empty if and only if $INIT_NAMENODE doesn't exist. So the -n test succeeds if $INIT_NAMENODE exists. The value 1 doesn't really matter here - it would do the same thing if you changed the 1 to 2 or 0 or teapot. All that matters is that it's not an empty string, since -n doesn't care about the rest.
Try some examples from your shell prompt: echo ${PATH+hello} should say hello because you do have a $PATH variable. echo ${asdfghjkl+hello} should print a blank line.
So, in the context of the if statement, the purpose of the test is to do the first echo if the variable $INIT_NAMENODE exists, and the second echo if the variable $INIT_NAMENODE doesn't exist.

create variables inside unix script

I am trying to create a variable in a script, based on another variable.
I just don't know what needs to be adjusted in my code, if it is possible.
I am simplifying my code for your understanding, so this is not the original code.
The code goes like that:
#!/bin/csh -f
set list_names=(Albert Bela Corine David)
set Albert_house_number=1
set Bela_house_number=2
set Corine_house_number=3
set David_house_number=4
foreach name ($list_names)
#following line does not work....
set house_number=$($name\_house_number)
echo $house_number
end
the desired output should be:
1
2
3
4
Thanks for your help.
Unfortunately, the bashism ${!varname} is not available to us in csh, so we'll have to go the old-fashioned route using backticks and eval. csh's quoting rules are different from those of POSIX-conforming shells, so all of this is csh specific. Here we go:
set house_number = `eval echo \" \$${name}_house_number\"`
echo "$house_number"
${name} is expanded into the backticked command, so this becomes equivalent to, say,
set house_number = `eval echo \" \$Albert_house_number\"`
which then evaluates
echo " $Albert_house_number"
and because of the backticks, the output of that is then assigned to house_number.
The space before \$$ is necessary in case the value of the expanded variable has special meaning to echo (such as -n). We could not simply use echo "-n" (it wouldn't print anything), but echo " -n" is fine.1
The extra space is stripped by csh when the backtick expression is expanded. This leads us to the remaining caveat: Spaces in variable values are going to be stripped; csh's backticks do that. This means that if Albert_house_number were defined as
set Albert_house_number = "3 4"
house_number would end up with the value 3 4 (with only one space). I don't know a way to prevent this.
1 Note that in this case, the echo "$house_number" line would have to be amended as well, or it would run echo "-n" and not print anything even though house_number has the correct value.

Bash variable defaulting doesn't work if followed by pipe (bash bug?)

I've just discovered a strange behaviour in bash that I don't understand. The expression
${variable:=default}
sets variable to the value default if it isn't already set. Consider the following examples:
#!/bin/bash
file ${foo:=$1}
echo "foo >$foo<"
file ${bar:=$1} | cat
echo "bar >$bar<"
The output is:
$ ./test myfile.txt
myfile.txt: ASCII text
foo >myfile.txt<
myfile.txt: ASCII text
bar ><
You will notice that the variable foo is assigned the value of $1 but the variable bar is not, even though the result of its defaulting is presented to the file command.
If you remove the innocuous pipe into cat from line 4 and re-run it, then it both foo and bar get set to the value of $1
Am I missing somehting here, or is this potentially a bash bug?
(GNU bash, version 4.3.30)
In second case file is a pipe member and runs as every pipe member in its own shell. When file with its subshell ends, $b with its new value from $1 no longer exists.
Workaround:
#!/bin/bash
file ${foo:=$1}
echo "foo >$foo<"
: "${bar:=$1}" # Parameter Expansion before subshell
file $bar | cat
echo "bar >$bar<"
It's not a bug. Parameter expansion happens when the command is evaluated, not parsed, but a command that is part of a pipeline is not evaluated until the new process has been started. Changing this, aside from likely breaking some existing code, would require extra level of expansion before evaluation occurs.
A hypothetical bash session:
> foo=5
> bar='$foo'
> echo "$bar"
$foo
# $bar expands to '$foo' before the subshell is created, but then `$foo` expands to 5
# during the "normal" round of parameter expansion.
> echo "$bar" | cat
5
To avoid that, bash would need some way of marking pieces of text that result from the new first round of pre-evaluation parameter expansion, so that they do not undergo a second
round of evaluation. This type of bookkeeping would quickly lead to unmaintainable code as more corner cases are found to be handled. Far simpler is to just accept that parameter expansions will be deferred until after the subshell starts.
The other alternative is to allow each component to run in the current shell, something that is allowed by the POSIX standard, but is not required, either. bash made the choice long ago to execute each component in a subshell, and reversing that would break too much existing code that relies on the current behavior. (bash 4.2 did introduce the lastpipe option, allowing the last component of a pipeline to execute in the current shell if explicitly enabled.)

Resources