I need a shell script program to print the hexadecimal number from big endian to little endian
For example
Input: my virtual address = 00d66d7e
Output: 7e6dd600
How can I can I create this in a bash script?
Just had to do this... but from decimal to little endian.. adapting that here:
echo 00d66d7e | tac -rs .. | echo "$(tr -d '\n')"
achieves the desired result, for arbitrarily sized hexadecimal representations of unsigned integers.
(h/t 'tac -rs' MestreLion, very nice!)
For 32 bit addresses, assuming it's zero padded:
v=00d66d7e
echo ${v:6:2}${v:4:2}${v:2:2}${v:0:2}
# 7e6dd600
Based on Karoly's answer you could use the following script, reading an argument or piped input:
#!/bin/bash
# check 1st arg or stdin
if [ $# -ne 1 ]; then
if [ -t 0 ]; then
exit
else
v=`cat /dev/stdin`
fi
else
v=$1
fi
i=${#v}
while [ $i -gt 0 ]
do
i=$[$i-2]
echo -n ${v:$i:2}
done
echo
For e.g. you could save this script as endian.sh and make it executable with:
chmod u+x endian.sh
Then:
echo 00d66d7e | ./endian.sh
gives you:
7e6dd600
For a different length string:
echo d76f411475428afc90947ee320 | ./endian.sh
result would be:
20e37e9490fc8a427514416fd7
#Update: Modified the script to accept the input either as an argument or from stdin, addressing Freewind's request. So now:
./endian.sh d76f411475428afc90947ee320
also works and gives you:
20e37e9490fc8a427514416fd7
This works for dash (and many other shells) :
v=0x12345678
v2=$(( (v<<8 & 0xff00ff00) | (v>>8 & 0xff00ff) ))
v2=$(( (v2<<16 & 0xffff0000) | v2>>16 ))
printf '0x%08x\n' $v2
Result should be "0x78563412"
${v:6:2} is for bash.
In response to Freewind's comment request and building off of hutheano's great answer, I wrote my own bash script and I include a condensed version below. The full script can be downloaded here.
The following implementation accounts for odd length strings, 0x or \x prefixes, and multiple output formats and can be used like the following:
$ be2le d76f411475428afc90947ee320 0xaaff 0xffa '\x3'
20e37e9490fc8a427514416fd7
0xffaa
0xfa0f
\x03
be2le bash script
#!/bin/bash
args=()
format=preserve
delimiter="\n"
nonewline=false
join=false
strip=false
while (( "$#" )); do
case "$1" in
-h|--help) usage;;
-f) format=$2; shift 2;;
--format=*) format="${1#*=}"; shift;;
-d) delimiter=$2; shift 2;;
--delimiter=*) delimiter="${1#*=}"; shift;;
-n|--no-newline) nonewline=true; shift;;
-j|--join) join=true; shift;;
-s|--strip-null) strip=true; shift;;
-*|--*) echo "Error: unsupported flag $1 specified"; exit 1;;
*) args=( "${args[#]}" "$1" ); shift;;
esac
done
case "$format" in
preserve);;
int) prefix="0x";;
char) prefix="\x";;
raw) ;;
*) echo "Error: unsupported format $format"; exit 1;;
esac
n=0
parts=()
for arg in ${args[#]}; do
digest=""
prefix=""
# remove prefix if string begins with "0x"
if [[ $arg =~ ^[0\\]x ]]; then
if [ "$format" == "preserve" ]; then
prefix=${arg:0:2}
fi
arg=${arg:2}
fi
# zero-pad if string has odd length
if [ $[${#arg} % 2] != 0 ]; then
arg="0$arg"
fi
part=""
i=${#arg}
while [ $i -gt 0 ]; do
i=$[$i-2]
byte=${arg:$i:2}
if [ $strip == true ] && [ -z "$part" ] && [ $byte == "00" ]; then
continue
fi
case "$format" in
int) part="$part"'0x'"$byte ";;
char) part="$part\x$byte";;
raw) part="$part$(printf "%b" "\x$byte")";;
*) part="$part$byte";;
esac
done
digest="$prefix$digest$part"
parts=( "${parts[#]}" "$digest" )
n=$[$n+1]
done
if [ $join == true ]; then
case "$format" in
*) printf "%s" "${parts[#]}";;
esac
else
i=0
for part in "${parts[#]}"; do
if [[ $(($i + 1)) < $n ]]; then
printf "%s$delimiter" "$part"
else
printf "%s" "$part"
fi
i=$(($i+1))
done
fi
if [ $nonewline == false ]; then
echo
fi
This script is for flipping 16 bit data.
#!/bin/bash
if [ -t 0 ]; then exit; fi
data=`cat /dev/stdin | od -An -vtx1 | tr -d ' ' | tr -d '\n'`
length=${#data}
i=0
while [ $i -lt $length ]; do
echo -n -e "\x${data:$[$i+2]:2}"
echo -n -e "\x${data:$[$i]:2}"
i=$[$i+4]
done
Related
i use a script that accepts parameter. parameters are optional and may occur in any order.
#!/bin/bash
# script name: test.sh
for var in "$#"
do
if [ ! -z "$var" ] && ([ $var = "--example" ] || [ $var = "-e" ]); then
echo "example"
elif [ ! -z "$var" ] && ([ $var = "--project" ] || [ $var = "-p" ]); then
echo "project with string xxxxxxx"
fi
done
in this simple example, you could call it like follows (some examples):
# this will echo example
./test.sh --example
# this will echo project with string xxxxxxx
./test.sh --project
# this will echo both example and project with string xxxxxxx
./test.sh --example --project
NOW, what i want to achieve is that i can do something like this (warning, this is pseuco code):
#!/bin/bash
# script name: test.sh
for var in "$#"
do
if [ ! -z "$var" ] && ([ $var = "--example" ] || [ $var = "-e" ]); then
echo "example"
elif [ ! -z "$var" ] && ([ $var = "--project" ] || [ $var = "-p" ]); then
echo "project with string $VAR_VALUE"
fi
done
# this will echo example
./test.sh --example
# this will echo project with string myproject1
./test.sh --project="myproject1"
# this will echo both example and project with string myproject2
./test.sh --example --project="myproject2"
can someone help me rewrite it so this will work somehow?
Use getopt. It handles short and long options, allows for both --long value and --long=value, decomposes -abc into -a -b -c, understands -- to end option parsing, and more.
#!/bin/bash
args=$(getopt -o ep: -l example,project: -n "$0" -- "$#") || exit
eval set -- "$args"
while [[ $1 != '--' ]]; do
case "$1" in
-e|--example) echo "example"; shift 1;;
-p|--project) echo "project = $2"; shift 2;;
# shouldn't happen unless we're missing a case
*) echo "unhandled option: $1" >&2; exit 1;;
esac
done
shift # skip '--'
echo "remaining non-option arguments: $#"
There are two possible path toward parsing argument list
Build custom option parser
use getopt, using 'long options'
The first approach is relatively simple (at this time). Using case instead of if to handle variants:
last_arg=
for arg in "$#"
do
if [ "$last_arg" = "-p" ] ; then
VAR_VALUE=$arg ;
last_arg=
echo "project with string $VAR_VALUE"
continue
fi
case "$arg" in
-e | --example)
echo "example" ;;
-p)
last_arg=$arg ;;
--project=*)
VAR_VALUE=${arg#*=}
echo "project with string $VAR_VALUE" ;;
*) ERROR-MESSAGE ;;
esac
done
exit
The BETTER approach is to leverage existing code. In particular getopt, which can handle long options:
#! /bin/bash
if T=$(getopt -o ep: --long 'example,project:' -n ${0#*/} -- "$#") ; then
eval set -- "$T"
else
exit $?
fi
while [ "$#" -gt 0 ] ; do
case "$1" in
-e | --example)
echo "example"
;;
-p | --project)
shift
VAR_VALUE=$1
echo "project with string $VAR_VALUE"
;;
--)
break
;;
*) echo "ERROR:$1" ;;
esac
shift
done
I'm working on a script that would allow me to add, remove, or edit config files. I have tested it a little and it seems like I got it to work at least with a single file, but I would like to be able to just do .config or fi.config and have it perform the desired action.
I would appreciate any help.
Config file looks looks similar to this just bigger
-- Config File
-- Environment DEV7
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- General properties
-------------------------------------------------------------------------------
com.x.yy.zz.version=2.0.2
com.x.yy.zz.instanceRole.ServerA=PRIMARY
com.x.yy.zz.instanceRole.ServerB=SECONDARY
com.x.yy.zz.StopDelay=30
com.x.yy.zz.sourceType=t
com.x.yy.zz.sNumberInc=20000
com.x.yy.zz.sNumberMin=20000
com.x.yy.zz.sNumberMax=9980000
so -a would allow me to add a line after something
ex. -a StopDealy New
com.x.yy.zz.StopDelay=30
New
#!/bin/bash
i=1
usage()
{
echo "test usage"
}
if [[ $# -gt 4 ]]
then
i=2
fi
while [[ $# -gt $i ]]
do
key="$1"
case $key in
-f|--file)
file="$2"
shift
;;
-a|--after)
sed -i "/$2/a $3" $file
#shift # past argument
;;
-b|--before)
sed -i "/$2/i $3" $file
#shift
;;
-d|--delete)
sed -i "/$2/d" $file
#shift
;;
-e|--edit)
sed -ie "s/$2/$3/g" $file
shift
;;
*)
usage
;;
esac
shift # past argument or value
done
I didn't test it yet, but this is the closest version to what I understand you want to achieve.
#!/bin/bash
usage() {
echo "Usage: $0 -f file1 -f *.txt -[abde] ... -f file3 ... -[abde] ... "
}
# Do all the required action on one file
do_work() {
file="$1" # 1st argument must be the file to work on
shift
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--file) while [[ ! "$2" = -* && $# -gt 0 ]]; do shift; done ;; # Ignore any "file" since we have one to work on.
-a|--after) sed -i "/$2/a $3" $file; shift 2 ;;
-b|--before) sed -i "/$2/i $3" $file; shift 2 ;;
-d|--delete) sed -i "/$2/d" $file; shift ;;
-e|--edit) sed -ie "s/$2/$3/g" $file; shift 2 ;;
esac
shift # past argument or value
done
}
# Check the arguments for files and print the valid ones
# Other arguments will just be skipped
# Invalid arguments will be displayed.
identify_files() {
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--file) # Validate the the file exists (just in case)
# check each following argument until next option
# ... or end of arguments
while [[ ! "$2" = -* && $# -gt 0 ]]; do
if [[ -f "$2" ]]; then
echo "$2"
else
echo "Error: Invalid file '$2'." >&2
fi
shift
done ;;
-[abe]) shift 2 ;;
-d) shift ;;
-h) usage >&2; exit ;;
*) echo "Invalid otpion '$1'" >&2 ;;
esac
shift
done
}
# Do the required actions on all files received in the options
for File in $(identify_files "$#"); do
do_work "$File" "$#"
done
## Alternative on predefined files (not the ones received as arguments)
## Usage: $0 -[abde] ...
#for File in $(ls *.config); do
# do_work "$File" "$#"
#done
The script is:
#!/bin/bash
# Dynamic Menu Function
createmenu () {
select selected_option; do # in "$#" is the default
if [ 1 -le "$REPLY" ] && [ "$REPLY" -le $(($#)) ]; then
break;
else
echo "Please make a vaild selection (1-$#)."
fi
done
}
declare -a drives=();
# Load Menu by Line of Returned Command
mapfile -t drives < <(lsblk --nodeps -o name,serial,size | grep "sd");
# Display Menu and Prompt for Input
echo "Available Drives (Please select one):";
createmenu "${drives[#]}"
# Split Selected Option into Array and Display
drive=($(echo "${selected_option}"));
echo "Drive Id: ${drive[0]}";
echo "Serial Number: ${drive[1]}";
The older system doesn't have mapfile or readarray so I need to convert that line to some alternative that can read each line of the lsblk output into an array.
The line in question that creates the array is:
mapfile -t drives < <(lsblk --nodeps -o name,serial,size | grep "sd");
You can loop over your input and append to the array:
$ while IFS= read -r line; do arr+=("$line"); done < <(printf '%d\n' {0..5})
$ declare -p arr
declare -a arr='([0]="0" [1]="1" [2]="2" [3]="3" [4]="4" [5]="5")'
Or, for your specific case:
while IFS= read -r line; do
drives+=("$line")
done < <(lsblk --nodeps -o name,serial,size | grep "sd")
See the BashFAQ/001 for an excellent explanation why IFS= read -r is a good idea: it makes sure that whitespace is conserved and backslash sequences not interpreted.
Here's the solution I came up with a while back. This is better because it provides a substitute function for older versions of Bash that don't support mapfile/readarray.
if ! type -t readarray >/dev/null; then
# Very minimal readarray implementation using read. Does NOT work with lines that contain double-quotes due to eval()
readarray() {
local cmd opt t v=MAPFILE
while [ -n "$1" ]; do
case "$1" in
-h|--help) echo "minimal substitute readarray for older bash"; exit; ;;
-r) shift; opt="$opt -r"; ;;
-t) shift; t=1; ;;
-u)
shift;
if [ -n "$1" ]; then
opt="$opt -u $1";
shift
fi
;;
*)
if [[ "$1" =~ ^[A-Za-z_]+$ ]]; then
v="$1"
shift
else
echo -en "${C_BOLD}${C_RED}Error: ${C_RESET}Unknown option: '$1'\n" 1>&2
exit
fi
;;
esac
done
cmd="read $opt"
eval "$v=()"
while IFS= eval "$cmd line"; do
line=$(echo "$line" | sed -e "s#\([\"\`]\)#\\\\\1#g" )
eval "${v}+=(\"$line\")"
done
}
fi
You don't have to change your code one bit. It just works!
readarray -t services -u < <(lsblk --nodeps -o name,serial,size | grep "sd")
For those playing along at home, this one aims to provide a mapfile that's feature-compliant with Bash 5, but still runs as far back as Bash 3.x:
#!/usr/bin/env bash
if ! (enable | grep -q 'enable mapfile'); then
function mapfile() {
local DELIM="${DELIM-$'\n'}"; opt_d() { DELIM="$1"; }
local COUNT="${COUNT-"0"}"; opt_n() { COUNT="$1"; }
local ORIGIN="${ORIGIN-"0"}"; opt_O() { ORIGIN="$1"; }
local SKIP="${SKIP-"0"}"; opt_s() { SKIP="$1"; }
local STRIP="${STRIP-"0"}"; opt_t() { STRIP=1; }
local FROM_FD="${FROM_FD-"0"}"; opt_u() { FROM_FD="$1"; }
local CALLBACK="${CALLBACK-}"; opt_C() { CALLBACK="$1"; }
local QUANTUM="${QUANTUM-"5000"}"; opt_c() { QUANTUM="$1"; }
unset OPTIND; local extra_args=()
while getopts ":d:n:O:s:tu:C:c:" opt; do
case "$opt" in
:) echo "${FUNCNAME[0]}: option '-$OPTARG' requires an argument" >&2; exit 1 ;;
\?) echo "${FUNCNAME[0]}: ignoring unknown argument '-$OPTARG'" >&2 ;;
?) "opt_${opt}" "$OPTARG" ;;
esac
done
shift "$((OPTIND - 1))"; set -- ${extra_args[#]+"${extra_args[#]}"} "$#"
local var="${1:-MAPFILE}"
### Bash 3.x doesn't have `declare -g` for "global" scope...
eval "$(printf "%q" "$var")=()" 2>/dev/null || { echo "${FUNCNAME[0]}: '$var': not a valid identifier" >&2; exit 1; }
local __skip="${SKIP:-0}" __counter="${ORIGIN:-0}" __count="${COUNT:-0}" __read="0"
### `while read; do...` has trouble when there's no final newline,
### and we use `$REPLY` rather than providing a variable to preserve
### leading/trailing whitespace...
while true; do
if read -d "$DELIM" -r <&"$FROM_FD"
then [[ ${STRIP:-0} -ge 1 ]] || REPLY="$REPLY$DELIM"
elif [[ -z $REPLY ]]; then break
fi
(( __skip-- <= 0 )) || continue
(( COUNT <= 0 || __count-- > 0 )) || break
### Yes, eval'ing untrusted content is insecure, but `mapfile` allows it...
if [[ -n $CALLBACK ]] && (( QUANTUM > 0 && ++__read % QUANTUM == 0 ))
then eval "$CALLBACK $__counter $(printf "%q" "$REPLY")"; fi
### Bash 3.x doesn't allow `printf -v foo[0]`...
### and `read -r foo[0]` mucks with whitespace
eval "${var}[$((__counter++))]=$(printf "%q" "$REPLY")"
done
}
### Alias `readarray` as well...
readarray() { mapfile "$#"; }
fi
if [[ -z ${PS1+YES} ]]; then
echo "'mapfile' should only be called as a shell function; try \"source ${BASH_SOURCE[0]##*/}\" first..." >&2
exit 1
fi
I have written a script that gets a variable number of arguments:
test.sh -i <input1> <input2> ... -o <output1> <output2> ...
I'm parsing the arguments as follows:
while [ $# -gt 1 ]; do
TMP=$(echo "$#" | cut -d '-' -f 2) #i <input1> <input2>
TMP1=$(echo "$TMP" | cut -d ' ' -f 1) #i
CNT=$(echo "$TMP" | wc -w) #3
set -x
case "$TMP1" in
i)
INPUTS=$(echo "$TMP" | cut -c 3-)
shift "$CNT"
;;
o)
OUTPUTS=$(echo "$TMP" | cut -c 3-)
shift "$CNT"
;;
esac
done
This works everytime, except for files that happen to have a '-' in their name.
Example:
./test.sh -i file1.txt file-2.txt -o out1.txt out-2.txt
Is there anyway I can force cut to ignore delimiters that occur within the file names?
You don't need all this string manipulation; each argument is already a separate word.
while (( $# > 0 )); do
case $1 in
-i) shift
while [[ $# -gt 0 && $1 != -* ]]; do
inputs+=( "$1" )
shift
done
;;
-o) shift
while [[ $# -gt 0 && $1 != -* ]]; do
outputs+=( "$1" )
shift
done
;;
*) echo "Unrecognized option $1"
exit 1
;;
esac
done
This can be refactored a little to avoid the repeated checks for running out of arguments.
for arg in "$#"; do
case $1 in
-i) mode=input; continue ;;
-o) mode=output; continue ;;
esac
case $mode in
input) input+=("$arg") ;;
output) output+=("$arg") ;;
*) echo "Unknown mode: $mode"
exit 1
;;
esac
done
Here's an alternative approach that may benefit someone.
The fact is, argument parsing is always a tradeoff, hence there's benefit in tailoring it to the application. Here's a pretty generic solution that allows a little bit of error checking and disorder in the arguments.
It's very simple, but I have added some example output and comments, and for the sake of readability and compatibility, stayed away from complex ways to save a line or two (especially on the if statements).
Sample Usage:
bash #> touch file-1 file3 file4 file-8 file7
bash #> argparse -i file-1 file3 file4 -c -k --q --j -r -t -o file-8 file7
Output:
Input files: file-1 file3 file4
Output files: file-8 file7
Args are: c k q j r t
Doing action for argument "c"
Doing action for argument "k"
Doing action for argument "j"
Script:
#!/bin/bash
#argparse
#Assign arrays
until [[ $# < 1 ]]; do
#ignore args "-i" and "-o", and tell the script to check for files following
if [ "$1" == "-i" ] ; then unset output ; input=1 ; shift
elif [ "$1" == "-o" ] ; then unset input ; output=1 ; shift
fi
#Add input and output files to respective arrays
if [ -f "$1" ] ; then
if [[ $input == 1 ]]; then
infiles+=($1)
elif [[ $output == 1 ]]; then
outfiles+=($1)
fi
else
#Add args to array
arg="$(echo "$1" | sed 's/-//g')"
args+=($arg)
fi
shift
done
#Some debug feedback
echo -e "Input files: ${infiles[#]}\nOutput files: ${outfiles[#]}\nArgs are: ${args[#]}\n"
#Simulate actually "doing" something with the args
for arg in "${args[#]}" ; do
case $arg in
"c") echo "Doing action for argument \"c\"" ;;
"k") echo "Doing action for argument \"k\"" ;;
"j") echo "Doing action for argument \"j\"" ;;
*) ;;
esac
done
Update/Edit: I've just realised, that the OP didn't have any requirement for parsing actual arguments other than -i and -o. Well regardless, this may still come in handy for someone at some point.
I am dealing with sorting words in Bash according to a given argument. I am given either argument -r, -a , -v or -h and according to it there are options to sort the words, as you can see at my "help".
Somehow, if I pass the argument -r it creates an error. I really don't understand what I am doing wrong, as if[["$arg"=="-a"]] works, but I have to use case somehow.
Here is my code:
#!/bin/bash
# Natalie Zubkova , zubkonat
# zubkonat#cvut.fel.cz , LS
#help
help="This script will calculate occurances of words in a given file, and it will sort them according to the given argument in following order> \n
without parametre = increasing order according to a number of occurance\n
-r = decreasing order according to a number of occurance\n
-a = in alphabetical increasing order\n
-a -r = in alphabetical decreasing order\n
There are also special cases of the given parametre, when the script is not sorting but:\n
-h = for obtaining help \n
-v = for obtaining a number of this task "
# this function will divide a given chain into a words, so we can start calculating the occurances, we also convert all the capital letters to the small ones by - tr
a=0;
r=0;
EXT=0;
if [ "$1" == "-h" ]; then
echo $help
exit 0
fi
if [ "$2" == "-h" ]; then
echo $help
exit 0
fi
if [ "$1" == "-v" ]; then
echo "5"
exit 0
fi
if [ "$2" == "-v" ]; then
echo "5"
exit 0
fi
function swap {
while read x y; do
echo "$y" "$x";
done
}
function clearAll {
sed -e 's/[^a-z]/\n/gI' | tr '[A-Z]' '[a-z]' | sort | uniq -c |awk '{i++; if(i!=1) print $2" "$1}' #swap
}
for arg do
case "$arg" in
"-a")
a=1
;;
"-r")
r=1
;;
"-v")
echo "5" #number of task is 5
exit 0
;;
"-h")
echo $help
exit 0
;;
"-?")
echo "invalid parametre, please display a help using argument h"
exit 0
;;
esac
done
#Sort according to parametres -a and -r
function sortWords {
if [[ a -eq 1 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk1
fi
fi
if [[ a -eq 1 ]]; then
if [[ r -eq 1 ]]; then
clearAll | sort -nk1 -r
fi
fi
if [[ r -eq 1 ]]; then
if [[ a -eq 0 ]]; then
clearAll | sort -nk2 -r
fi
fi
if [[ a -eq 0 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk2
fi
fi
}
#code is from Stackoverflow.com
function cat-all {
while IFS= read -r file
do
if [[ ! -z "$file" ]]; then
cat "$file"
fi
done
}
#histogram
hist=""
for arg do
if [[ ! -e "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -f "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -r "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
fi
done
for arg do
hist="$hist""$arg""\n"
done
echo -e "$hist" | cat-all | sortWords
exit $EXT;
Here is what our upload system which does some test to see if our program works says:
Test #6
> b5.sh -r ./easy.txt
ERROR: script output is wrong:
--- expected output
+++ script stdout
## --- line 1 (167 lines) ; +++ no lines ##
-the 89
-steam 46
-a 39
-of 37
-to 35
...
script written 484 lines, while 484 lines are expected
script error output:
A FILE DOESNT EXIST
cat: invalid option -- 'r'
Try `cat --help' for more information.
script exit value: 1
ERROR: Interrupted due to failed test
If anyone could help me I would really appreciate it.
You forgot to move the parameter index position with shift:
"-r")
r=1
shift
;;
shift above moves to the next command line arg: ./easy.txt in your case.
Without it, read -r file will read -r instead of the file name.