Linux shell script - String trimming for dash - linux

I am attempting to get the mac address from my Raspberry Pi take the last 6 characters of the mac to use as the hostname alongside a fixed string.
here is what I'v managed to get working from other sources so far, but I am now totally stuck trying to trim the string down.
#!/bin/sh -e
MAC="$( sed "s/^.*macaddr=\([0-9A-F:]*\) .*$/\1/;s/://g" /proc/cmdline )"
MAC1="${MAC??????%}"
echo "$MAC1"
the shell being used by the Pi appears to be Dash, so the usual BASH commands that would have this done in no-time don't want to work or seem to generate errors when run within the script.
The full script that I am using in rc.local is below.
any advice on a way to-do this would be greatly received.
MAC="pi""$( sed "s/^.*macaddr=\([0-9A-F:]*\) .*$/\1/;s/://g" /proc/cmdline )"
echo "$MAC" > "/etc/hostname"
CURRENT_HOSTNAME=$(cat /proc/sys/kernel/hostname)
sed -i "s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\t$MAC/g" /etc/hosts
hostname $MAC

If you have the cut command on your Pi, you could
do
MAC1=$( echo $MAC | cut -c 7-12 )

Since you're already using sed to process the string, I'd suggest adding another command:
MAC=$(sed -e 's/^.*macaddr=\([0-9A-F:]*\) .*$/\1/' \
-e 's/://g' \
-e 's/.*\(.\{6\}\)/\1/' /proc/cmdline)
The extra sed command extracts the last 6 characters from each line (I assume that you only have one?). You can combine the commands into a single string if you prefer, though I find this approach to be more readable.

Related

String starts again after every variable [Bash]

I'm trying to use multiple variables in a string but after each variable the string starts again and overrides the beginning:
#!/bin/bash
var1="ABCDEFG"
var2="hi"
echo "${var1} ${var2}"
echo "$var1 $var2"
It should output
ABCDEFG hi
But both echos output
hiDEFG
Also if I only use one variable and put text after the variable it still overrides...
There is also an example here:
https://stackoverflow.com/a/17862845/8363344
bla=hello
laber=kthx
echo "${bla}ohai${laber}bye"
This should output:
helloohaikthxbye
But it outputs:
byeikthx
I'm starting the .sh with
sudo bash path/bash.sh
But with sudo sh it does not work as well...
I use Ubuntu 16.04 (as a virtual machine)
Thanks
Dennis
It might be a carriage return character in your input string(s).
You can pipe your echo to a tr command to remove it from output string:
echo "${var1} ${var2}" | tr -d '\r'

How to get list of commands used in a shell script?

I have a shell script of more than 1000 lines, i would like to check if all the commands used in the script are installed in my Linux operating system.
Is there any tool to get the list of Linux commands used in the shell script?
Or how can i write a small script which can do this for me?
The script runs successfully on the Ubuntu machine, it is invoked as a part of C++ application. we need to run the same on a device where a Linux with limited capability runs. I have identified manually, few commands which the script runs and not present on Device OS. before we try installing these commands i would like to check all other commands and install all at once.
Thanks in advance
I already tried this in the past and got to the conclusion that is very difficult to provide a solution which would work for all scripts. The reason is that each script with complex commands has a different approach in using the shells features.
In case of a simple linear script, it might be as easy as using debug mode.
For example: bash -x script.sh 2>&1 | grep ^+ | awk '{print $2}' | sort -u
In case the script has some decisions, then you might use the same approach an consider that for the "else" cases the commands would still be the same just with different arguments or would be something trivial (echo + exit).
In case of a complex script, I attempted to write a script that would just look for commands in the same place I would do it myself. The challenge is to create expressions that would help identify all used possibilities, I would say this is doable for about 80-90% of the script and the output should only be used as reference since it will contain invalid data (~20%).
Here is an example script that would parse itself using a very simple approach (separate commands on different lines, 1st word will be the command):
# 1. Eliminate all quoted text
# 2. Eliminate all comments
# 3. Replace all delimiters between commands with new lines ( ; | && || )
# 4. extract the command from 1st column and print it once
cat $0 \
| sed -e 's/\"/./g' -e "s/'[^']*'//g" -e 's/"[^"]*"//g' \
| sed -e "s/^[[:space:]]*#.*$//" -e "s/\([^\\]\)#[^\"']*$/\1/" \
| sed -e "s/&&/;/g" -e "s/||/;/g" | tr ";|" "\n\n" \
| awk '{print $1}' | sort -u
the output is:
.
/
/g.
awk
cat
sed
sort
tr
There are many more cases to consider (command substitutions, aliases etc.), 1, 2 and 3 are just beginning, but they would still cover 80% of most complex scripts.
The regular expressions used would need to be adjusted or extended to increase precision and special cases.
In conclusion if you really need something like this, then you can write a script as above, but don't trust the output until you verify it yourself.
Add export PATH='' to the second line of your script.
Execute your_script.sh 2>&1 > /dev/null | grep 'No such file or directory' | awk '{print $4;}' | grep -v '/' | sort | uniq | sed 's/.$//'.
If you have a fedora/redhat based system, bash has been patched with the --rpm-requires flag
--rpm-requires: Produce the list of files that are required for the shell script to run. This implies -n and is subject to the same limitations as compile time error checking checking; Command substitutions, Conditional expressions and eval builtin are not parsed so some dependencies may be missed.
So when you run the following:
$ bash --rpm-requires script.sh
executable(command1)
function(function1)
function(function2)
executable(command2)
function(function3)
There are some limitations here:
command and process substitutions and conditional expressions are not picked up. So the following are ignored:
$(command)
<(command)
>(command)
command1 && command2 || command3
commands as strings are not picked up. So the following line will be ignored
"/path/to/my/command"
commands that contain shell variables are not listed. This generally makes sense since
some might be the result of some script logic, but even the following is ignored
$HOME/bin/command
This point can however be bypassed by using envsubst and running it as
$ bash --rpm-requires <(<script envsubst)
However, if you use shellcheck, you most likely quoted this and it will still be ignored due to point 2
So if you want to use check if your scripts are all there, you can do something like:
while IFS='' read -r app; do
[ "${app%%(*}" == "executable" ] || continue
app="${app#*(}"; app="${app%)}";
if [ "$(type -t "${app}")" != "builtin" ] && \
! [ -x "$(command -v "${app}")" ]
then
echo "${app}: missing application"
fi
done < <(bash --rpm-requires <(<"$0" envsubst) )
If your script contains files that are sourced that might contain various functions and other important definitions, you might want to do something like
bash --rpm-requires <(cat source1 source2 ... <(<script.sh envsubst))
Based #czvtools’ answer, I added some extra checks to filter out bad values:
#!/usr/bin/fish
if test "$argv[1]" = ""
echo "Give path to command to be tested"
exit 1
end
set commands (cat $argv \
| sed -e 's/\"/./g' -e "s/'[^']*'//g" -e 's/"[^"]*"//g' \
| sed -e "s/^[[:space:]]*#.*\$//" -e "s/\([^\\]\)#[^\"']*\$/\1/" \
| sed -e "s/&&/;/g" -e "s/||/;/g" | tr ";|" "\n\n" \
| awk '{print $1}' | sort -u)
for command in $commands
if command -q -- $command
set -a resolved (realpath (which $command))
end
end
set resolved (string join0 $resolved | sort -z -u | string split0)
for command in $resolved
echo $command
end

Ubuntu Bash Script executing a command with spaces

I have a bit of an issue and i've tried several ways to fix this but i can't seem to.
So i have two shell scripts.
background.sh: This runs a given command in the background and redirect's output.
#!/bin/bash
if test -t 1; then
exec 1>/dev/null
fi
if test -t 2; then
exec 2>/dev/null
fi
"$#" &
main.sh: This file simply starts the emulator (genymotion) as a background process.
#!/bin/bash
GENY_DIR="/home/user/Documents/MyScript/watchdog/genymotion"
BK="$GENY_DIR/background.sh"
DEVICE="164e959b-0e15-443f-b1fd-26d101edb4a5"
CMD="$BK player --vm-name $DEVICE"
$CMD
This works fine when i have NO spaces in my directory. However, when i try to do: GENY_DIR="home/user/Documents/My Script/watchdog/genymotion"
which i have no choice at the moment. I get an error saying that the file or directory cannot be found. I tried to put "$CMD" in quote but it didn't work.
You can test this by trying to run anything as a background process, doesn't have to be an emulator.
Any advice or feedback would be appreciated. I also tried to do.
BK="'$BK'"
or
BK="\"$BK\""
or
BK=$( echo "$BK" | sed 's/ /\\ /g' )
Don't try to store commands in strings. Use arrays instead:
#!/bin/bash
GENY_DIR="$HOME/Documents/My Script/watchdog/genymotion"
BK="$GENY_DIR/background.sh"
DEVICE="164e959b-0e15-443f-b1fd-26d101edb4a5"
CMD=( "$BK" "player" --vm-name "$DEVICE" )
"${CMD[#]}"
Arrays properly preserve your word boundaries, so that one argument with spaces remains one argument with spaces.
Due to the way word splitting works, adding a literal backslash in front of or quotes around the space will not have a useful effect.
John1024 suggests a good source for additional reading: I'm trying to put a command in a variable, but the complex cases always fail!
try this:
GENY_DIR="home/user/Documents/My\ Script/watchdog/genymotion"
You can escape the space with a backslash.

how to use sed command with several slashes in bash

I am trying to use SED command with a variable that contains several / and i got the following error :
sed: -e expression 1, char 16: unknown option to s
this is my code and this is inside a script:
thispath=$(readlink -f $0)
sudo sed -i '13s/^/'"$thispath"'/g' /etc/rc.local
and the variable contains for example: /home/user/script.sh
i do not know very well the use of sed can somebody help me
The s command is sed allows you to use any character to delimit the regex and replacement parts. "/" is often a poor choice given how often you come across it in UNIX paths:
Try:
sudo sed -i '13s:^:'"$thispath"':g' /etc/rc.local
It is dangerous to do this directly on rc.local. So make a copy first.

sed command on single line input

I am using SunOS 10. I am trying to replace the : character at the end of line if the word contains : in it.
I am using the below command for it.
echo -n "test:" | sed 's/:$//g'
It's not working. What did I do wrong here?
The same command is working fine in GNU/Linux.
You don't need a line feed. You need to remove that -n
echo "test:" | sed 's/:$//g'
myshell:/home/myfolderpath # echo -n "test:"|sed 's/:$//g'
testmyshell:/home/myfolderpath#
you code works on my machine.
because there is no tailing new line.you gonna see the result right before your next shell command line. -n is not necessary.
myshell:/home/myfolderpath # echo "test:"|sed 's/:$//g'
test
myshell:/home/myfolderpath#
it should be like this without -n

Resources