Read commandline argument by name - linux

If I run a bash script as
./myscript.sh zone=A build=release
Is there someway I can read the arguments based on the parameters instead of using $1, $2?

No. Unix shells are an ancient technology, hash maps were known at the time but not "en vogue." They needed more than 1 byte to implement so the professionals didn't want to use such a wasteful technology.
What else can you do? The usual solution is getopt(1).
An alternative is to write all options to a file and source that:
echo "$#" | tr ' ' '\n' > options
. ./options
echo "zone=${zone}"

Hope this is what you are asking for:
A="try"
try="something"
echo ${!A}
> something

This is close to what you want.
set -k
./myscript.sh zone=A build=release
will behave the same as
zone=A build=release ./myscript.sh
that is, myscript.sh will see zone and build with the given values in its environment.

Related

How do i use other command in the same line with echo

I want to use a command to be printed on the same line as the string.
for example, i want to print something like:
hostname: cpu1
but when i use the command like this it doesnt work
echo 'hostname:' hostname
You need to use $() to evaluate:
echo 'hostname:' $(hostname)
Two answers are already given saying that you "need to" and "should" use command substitution by doing:
echo "hostname: $(hostname)"
but I will disagree with both. Although that works, it is aesthetically unappealing. That command instructs the shell to run hostname and read its output, and then pass that output as part of the argument to echo. But all that work is unnecessary. For this simple use case, it is "simpler" to do:
printf "hostname: "; hostname
(Using printf to suppress the newline, and avoiding echo -n because echo really should be treated as deprecated). This way, the output of hostname goes directly to the shell's stdout without any additional work being done by the shell. I put "simpler" in quotes because an argument could be made that humans find printf "hostname: %s\n" "$(hostname)" or echo "hostname: $(hostname)" to be simpler, and perhaps looking at much code that does things that way warps your mind and even makes it look simpler, but a few moments reflection should reveal that indeed it is not.
OTOH, there are valid reasons for collecting the output and writing the message with echo/printf. In particular, by doing it that way, the message will (most likely) be written with one system call and not be subject to interleaving with messages from other processes. If you printf first and then execute hostname, other processes' data may get written between the output of printf and the output of hostname. As always, YMMV.
This is what you should do:
echo "hostname: `hostname`"
Words enclosed in backticks are read by the command line as commands even when they are inside a string, like in this case.
Have a great day! :)

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.

Using "read" to set variables

In bash from the CLI I can do:
$ ERR_TYPE=$"OVERLOAD"
$ echo $ERR_TYPE
OVERLOAD
$ read ${ERR_TYPE}_ERROR
1234
$ echo $OVERLOAD_ERROR
1234
This works great to set my variable name dynamically; in a script it doesn't work. I tried:
#!/bin/env bash
ERR_TYPE=("${ERR_TYPE[#]}" "OVERLOAD" "PANIC" "FATAL")
for i in "${ERR_TYPE[#]}"
do
sh -c $(echo ${i}_ERROR=$"1234")
done
echo $OVERLOAD_ERROR # output is blank
# I also tried these:
# ${i}_ERROR=$(echo ${i}_ERROR=$"1234") # command not found
# read ${i}_ERROR=$(echo ${i}_ERROR=$"1234") # it never terminates
How would I set a variable as I do from CLI, but in a script? thanks
When you use dynamic variables names instead of associative arrays, you really need to question your approach.
err_type=("OVERLOAD" "PANIC" "FATAL")
declare -A error
for type in "${err_type[#]}"; do
error[$type]=1234
done
Nevertheless, in bash you'd use declare:
declare "${i}_error=1234"
Your approach fails because you spawn a new shell, passing the command OVERLOAD_ERROR=1234, and then the shell exits. Your current shell is not affected at all.
Get out of the habit of using ALLCAPSVARNAMES. One day you'll write PATH=... and then wonder why your script is broken.
If the variable will hold a number, you can use let.
#!/bin/bash
ERR_TYPE=("OVERLOAD" "PANIC" "FATAL")
j=0
for i in "${ERR_TYPE[#]}"
do
let ${i}_ERROR=1000+j++
done
echo $OVERLOAD_ERROR
echo $PANIC_ERROR
echo $FATAL_ERROR
This outputs:
1000
1001
1002
I'd use eval.
I think this would be considered bad practice though (it had some thing to do with the fact that eval is "evil" because it allows bad input or something):
eval "${i}_ERROR=1234"

Making the script issue "enter" after executing command in shell script [duplicate]

I've created a really simple bash script that runs a few commands.
one of these commands needs user input during runtime. i.e it asks the user "do you want to blah blah blah?", I want to simply send an enter keypress to this so that the script will be completely automated.
I won't have to wait for the input or anything during runtime, its enough to just send the keypress and the input buffer will handle the rest.
echo -ne '\n' | <yourfinecommandhere>
or taking advantage of the implicit newline that echo generates (thanks Marcin)
echo | <yourfinecommandhere>
You can just use yes.
# yes "" | someCommand
You might find the yes command useful.
See man yes
Here is sample usage using expect:
#!/usr/bin/expect
set timeout 360
spawn my_command # Replace with your command.
expect "Do you want to continue?" { send "\r" }
Check: man expect for further information.
You could make use of expect (man expect comes with examples).
I know this is old but hopefully, someone will find this helpful.
If you have multiple user inputs that need to be handled you can use process substitution and use echo as a 'file' for cat with whatever is needed to handle the first input like this:
# cat ignores stdin if it has a file to look at
cat <(echo "selection here") | command
and then you can handle subsequent inputs by piping the yes command with the answer:
cat <(echo "selection here") | yes 'y' | command

Create bash script that takes input

I want to create a bash script that is simular to a programming interpreter like mongo, node, redis-cli, mysql, etc.
I want to be able to use a command like test and it behave like the examples above.
thomas#workstation:~$ test
>
How do I make a command that behaves like this? What is this called?
I want to be able to take the content and turn it into a variable.
thomas#workstation:~$ test
> hello world
hello world
thomas#workstation:~$
I only want to take one "entry" after enter is pressed once I want to be able to process the string "hello world" in the code, like echo it.
What is this called? How do I make one using BASH?
I think "read" is what you are looking for, isn't it?
here is a link with some examples: http://bash.cyberciti.biz/guide/Getting_User_Input_Via_Keyboard
so you can do stuff like this:
read -p "Enter your name : " name
echo "Hi, $name. Let us be friends!"
I'm sorry this doesn't answer you directly, but it might be worth it to look into using a more fully capable programming language such as Python, Ruby, or Perl for a task like this. In Python you can use the raw_input() function.
user_command = raw_input('> ')
would yield your prompt.
First, do not name your script test. That generates too much confusion. Whatever you call it, you can do many things:
#!/bin/sh
printf '> '
read line
echo "$line"
If your shell supports it:
#!/bin/sh
read -p '> ' line
echo "$line"
or
#!/bin/sh
printf '> '
sed 1q # This will print the input. To store in in a variable: a=$( sed 1q )
[spatel#tux ~]$ read a
Hello World!!!!!
[spatel#tux ~]$ echo $a
Hello World!!!!!
Key word that might be useful here is REPL (Read–eval–print loop) used primarily for programming languages or coding environments. Your browsers console is a great example of a REPL.
Node allows you use their REPL to build interactive apps.

Resources