How use more or less inside bash script - linux

Okay, I have big list of servers and I wrote a bash script to enlist them. But the problem is that it scroll through the terminal.
I need a pagination view of those.
Is it possible to put more or less inside bash script?
say ,
1
.
.
.
1000
One page at a time.
I have this code:
Staging_Servers)
echo " Staging server list should go here"
;;
UAT_Servers)
echo " UAT server list should go here"
;;
Prod_Servers)
echo " Prod server list should go here"
;;
And one of them have long listing.
Once I run the script it shows menu like below:
1) Jenkin_Servers 5) Prod_Servers 9) DB-Staging
2) Nagios_Servers 6) Proxy_Servers 10) DB-Prod
3) Staging_Servers 7) Dedicated_Servers
4) UAT_Servers 8) Shared_Servers
Please enter the required number :
Okay once I give the number to the prompt one of the menu has lot of items which scroll of the screen. I want to see it by one page at a time.

Yes, you can invoke more or less in a bash script:
#!/bin/bash
...
cat file* | less
...
will start paginating output once it reaches the cat (or whatever command it is).
I used to write README files that had a first line consisting of:
#!/usr/bin/less
When you executed the README, it would paginate the README. Self paginating text files.

Store your server list in a string or array, and echo the output through your PAGER, or explicitly through less. For example:
export PAGER='/usr/bin/less'
case $list_name in
Staging_Servers)
echo "${staging_servers[#]}" | "$PAGER"
;;
UAT_Servers)
echo "${uat_servers[#]}" | "$PAGER"
;;
Prod_Servers)
echo "${prod_servers[#]}" | "$PAGER"
;;
esac

Related

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.

Simulating ENTER key and export another variable together in Linux bash

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

Why is formatting for blocks of text showing up weirdly?

I was wondering if you could help me with something.
I want to write a menu for one of the scripts I'm working on. Currently, my script is:
echo "########################################"
echo "# Script tasks #"
echo "# #"
echo "# 1 Show running processes #"
echo "# 2 Show logged in users #"
... (continued)
echo "########################################"
However, when I run this inside of my script, for some reason some of the # signs at the end of the line get either indented into the box, or pushed further out, resulting in the right side of the box looking very bad and not well thought-out. I would like the right side of the box to look even (i.e., actually like a box).
I'm using Bash on Ubuntu 14.04 LTS and gedit as my text editor.
This isn't your question, but the way to do menus in a shell script is with the select command
# an array with the menu choices
choices=(
"Show running processes"
"Show logged in users"
"..."
)
# define the prompt
PS3="What is your choice? "
# display and get user input
# select is an infinite loop: `break` when you've done something successfully
select choice in "${choices[#]}"; do
case "$choice" in
"Show running processes")
some code here
break
;;
"Show logged in users")
some code here
break
;;
*)
echo "Please select a number from the menu."
# do not break, select will re-display
;;
esac
done
# if you need the choice afterward, you still have it
echo "$choice"

If I create a menu in Bash is command style history possible

I have no problem creating the menu - that is not what this question is about.
What happens however is I go from having the arrow keys be useful (scroll up and down to get access to previous commands I've run at the command line) to completely useless (^[[A^[[A^[[A^[[B^[[C^[[D^[[C)
Is there any way to encapsulate that behaviour into a menu?
E.g. can I use the scroll up and down keys to access previous options I've selected. (It's a BIG menu and I have MANY options like dev.client.alpha or dev.otherclient.beta etc...)
I supposed I could break each one into separate files and just use the command line diredtly OR I could pass an augment to the menu so as to call: ~/menu dev.clint.alpha directly from the command line.
Just curious is anyone else has (had) this itch and if anything has ever been done about it?
Menu I'm presently using is done basically as follows:
while :
clear
do
echo "$MENU"
read CHOICE ARG1 ARG2 ARG3 ARG4 overflow
case $CHOICE in
command.a)
# do stuff here
;;
command.b)
# do different stuff here
;;
*) # catch all...
continue
;;
esac
done
clear
You can do what you want by enabling readline on your read,
and appending each choice reply to the internal history. You can even save
the history to a file so when you rerun the script you have the old
history. For example:
HISTFILE=/tmp/myhistory
history -r # read old history
while :
do echo "MENU. a b q"
read -e # sets REPLY, enables readline
history -s "$REPLY" # add to history
history -w # save to file
set -- $REPLY
CHOICE=$1; shift
ARG1=$1 ARG2=$2 ARG3=$3 ARG4=$4; shift 4
overflow="$*"
case $CHOICE in
a) echo do stuff here $ARG1 ;;
b) echo do different stuff here ;;
q) exit ;;
*) echo catch all...
continue ;;
esac
done

Bash scripting on debian installer not accepting user input on preseeding

I have a very small script that needs to be run on debian installer: (via preseeding, pre installation script)
echo -n -e " # Your option [1] [2] [3]: "
read REPLY
if [ "$REPLY" == "1" ]
The script stops here and whatever I press is just displayed onto screen however it is not accepting the enter key. Normally, when you press 1 and press enter, the read should return 1 to $REPLY. But nothing happens. It keeps accepting user input but no further action happens.
Then, I switched to tty2 with ALT+F2 and run the script there, it was fine, it works as expected, when I press; it takes the input. Why tty1 is not accepting enter as usual?
Use debconf for that kind of configuration, it tackles exactly needs like yours.
Adapted example from the manual
Template file (debian/templates):
Template: your_package/select_option
Type: select
Choices: 1, 2, 3
Description: Which option?
Choose one of the options
Script (debian/config):
#!/bin/sh -e
# Source debconf library.
. /usr/share/debconf/confmodule
db_input medium your_package/select_option || true
db_go
# Check their answer.
db_get your_package/select_option
if [ "$RET" = "1" ]; then
# Do stuff
fi
Had the same problem (read not processing my input) with busybox on an embedded Linux.
Took me some time to realize that busybox's read is not CR-tolerant — my terminal program (used miniterm.py) sent CR/LF line ends by default; switching it to LF only solved my problem!
with bash interpreter, try replace read by :
builtin read
with other sh interpreter, specify the variable name :
read REPLY
The following script works fine for me:
#!/bin/sh
echo -n -e " # Your option [1] [2] [3]: "
read
case $REPLY in
1 )
echo "one" ;;
2 )
echo "two" ;;
3 )
echo "three" ;;
*)
echo "invalid" ;;
esac
It prints out one nicely if I choose 1. Any reason why you'd like to stick to if...fi?

Resources