Position of a string within a string using Linux shell script? - linux

If I have the text in a shell variable, say $a:
a="The cat sat on the mat"
How can I search for "cat" and return 4 using a Linux shell script, or -1 if not found?

With bash
a="The cat sat on the mat"
b=cat
strindex() {
x="${1%%"$2"*}"
[[ "$x" = "$1" ]] && echo -1 || echo "${#x}"
}
strindex "$a" "$b" # prints 4
strindex "$a" foo # prints -1
strindex "$a" "ca*" # prints -1

You can use grep to get the byte-offset of the matching part of a string:
echo $str | grep -b -o str
As per your example:
[user#host ~]$ echo "The cat sat on the mat" | grep -b -o cat
4:cat
you can pipe that to awk if you just want the first part
echo $str | grep -b -o str | awk 'BEGIN {FS=":"}{print $1}'

I used awk for this
a="The cat sat on the mat"
test="cat"
awk -v a="$a" -v b="$test" 'BEGIN{print index(a,b)}'

echo $a | grep -bo cat | sed 's/:.*$//'

This can be accomplished using ripgrep (aka rg).
❯ a="The cat sat on the mat"
❯ echo $a | rg --no-config --column 'cat'
1:5:The cat sat on the mat
❯ echo $a | rg --no-config --column 'cat' | cut -d: -f2
5
If you wanted to make it a function you can do:
function strindex() {
local str=$1
local substr=$2
echo -n $str | rg --no-config --column $substr | cut -d: -f2
}
...and use it as such: strindex <STRING> <SUBSTRING>
strindex "The cat sat on the mat" "cat"
5
You can install ripgrep on MacOS with: brew install --formula ripgrep.

This is just a version of the glenn jackman's answer with escaping, the complimentary reverse function strrpos and python-style startswith and endswith function based on the same principle.
Edit: updating escaping per #bruno's excellent suggestion.
strpos() {
haystack=$1
needle=$2
x="${haystack%%"$needle"*}"
[[ "$x" = "$haystack" ]] && { echo -1; return 1; } || echo "${#x}"
}
strrpos() {
haystack=$1
needle=$2
x="${haystack%"$needle"*}"
[[ "$x" = "$haystack" ]] && { echo -1; return 1 ;} || echo "${#x}"
}
startswith() {
haystack=$1
needle=$2
x="${haystack#"$needle"}"
[[ "$x" = "$haystack" ]] && return 1 || return 0
}
endswith() {
haystack=$1
needle=$2
x="${haystack%"$needle"}"
[[ "$x" = "$haystack" ]] && return 1 || return 0
}

Most simple is -
expr index "The cat sat on the mat" cat
it will return 5

Related

How to write a function that returns ASCII of character without printf in bash

(Printf "%d\n" \'A) this code is printing the ASCII but I my working with function I want function to return ASCII I am struggling with this .
Please help me.
You can use the POSIX utility od:
$ echo A | tr -d "\n" | od -An -t uC
65
or hexdump:
$ echo Z | tr -d "\n" | hexdump -d | head -1 | cut -d " " -f 4
00090
Or you could loop through the A-z letters I suppose (no error correction here...):
ord() {
local ordinal=65
for c in {A..z};
do
if [ "$1" = "$c" ]
then
break
fi
let ordinal=ordinal+1
done
echo "$ordinal"
}
$ ord z
122
Or the more 'Bashy':
ord() {
local ordinal=65
for c in {A..z};
do
[[ "$1" == "$c" ]] && break
(( ordinal++ ))
done
echo "$ordinal"
}
Bottom line: use printf:
$ ord() { LC_CTYPE=C printf '%d\n' "'$1"; }
ord A
65

Bash function not returning value to the script while running through cron

I have a main script that calls a function , which is written in another file.I have sourced this file inside the main script. When I run this main script using cron, then the function does not return any value. Whereas if i run the same main script in console , the function returns value and prints on console.
Below is my main script.
source /home/employee/conf/myConf.cfg $0
load_dte=`fnGetDate dir1 dir2/*`
fnLog "load_dte $load_dte"
And configuration file that contains the function:
function fnGetDate(){
x="2016-06-21"
echo "$x"
}
When this main script is scheduled via cron :
Output is :
load_dte
Whithout cron output:
load_dte 2016-06-21
Any help is appreciated.
Edit:
Below is the full codebase:
$1 $2 are the hdfs locations/tmp/hdfs/loc1 and /tmp/hdfs/loc2
main script:
#/bin/bash
PATH=/bin:/usr/bin
dirname=$( cd "$( dirname "$0" )" && pwd )
source `echo ${dirname%/*}`/conf/lib.cfg $
fnGetScriptPath $0
fnCreateLogFile ${0}
fnMain(){
TBL_BUS="$1"
TBL_ENT="$2"
load_dte=fnGetDate "/tmp/hdfs/loc1" "/tmp/hdfs/loc2*" ""
echo load_dte $load_dte
fnLog "load_dte $load_dte" "$C_INFO"
}
fnMain $1 $2
Below is the config file:
SCRIPT_NAME="$1"
CURRENT_DATE=`date +%Y-%m-%d`
C_INFO=0
C_SUCCESS=1
C_DEBUG=2
C_WARN=3
C_ERROR=4
LOG_STATUS=(INFO SUCCESS DEBUG WARN ERROR)
function fnCreateLogFile(){
fnGetScriptPath $1
FILE=`basename $1`
LOGDIR=$root_dir/logs
LOGFILE=${LOGDIR}/${FILE/'.sh'/''}_${CURRENT_DATE}.log
if [[ ! -d $LOGDIR ]]; then
mkdir -p $LOGDIR
fnLog "Creating log dir: $LOGDIR" $C_SUCCESS
else
fnLog "Log file : $(cd `dirname $LOGFILE` && pwd)/$FILENAME" "$C_INFO"
fi
}
function fnGetScriptPath(){
script_name=`basename $1`
script_base_name=`echo $script_name | cut -d'.' -f1`
dir_name=`dirname $1`
if [[ $dir_name == "." ]];then
work_dir=`pwd`
root_dir=`pwd | awk 'BEGIN{FS="/"; OFS = "/"} {$NF="";print}' | sed 's/.$//'`
else
work_dir=$dir_name
root_dir=`echo $dir_name | awk 'BEGIN{FS="/"; OFS = "/"} {$NF="";print}' | sed 's/.$//'`
fi
}
function fnLog() {
echo `date +"%Y-%m-%d %k:%M:%S"` $1
echo `date +"%Y-%m-%d %k:%M:%S"` $1 >> $LOGFILE
if [[ $2 -eq 1 || $2 -eq 2 ]]; then MSG_ACCUM="$MSG_ACCUM \\n $1" ;fi
}
function fnGetDate(){
HDFS_SRC=$1
HDFS_TGT=$2
SKIP=${3:-"0"}
for X in ${!HDFS_SRC[*]}
do
#echo "comm -23 <(hadoop fs -ls ${HDFS_SRC[X] } | awk -F'/' '{match(\$NF, \"[0-9]+-[0-9]+-[0-9]+\", v)}{print v[0]}' | sort) <( hadoop fs -ls $HDFS_TGT | awk -F'/' '{match(\$NF, \"[0-9]+-[0-9]+-[0-9]+\", v)}{print v[0]}'"
HDFS=("${HDFS[#]}" "`comm -23 <(hadoop fs -ls ${HDFS_SRC[X] } | awk -F'/' '{match($NF, "[0-9]+-[0-9]+-[0-9]+", v)}{print v[0]}' | sort) <( hadoop fs -ls $HDFS_TGT | awk -F'/' '{match($NF, "[0-9]+-[0-9]+-[0-9]+", v)}{print v[0]}' | sort ) | grep "^[0-9]"`")
done
if [[ $value == "" ]];then
DATE=( $(
for x in "${HDFS[#]}"
do
echo "$x"
done | sort | uniq) )
echo ${DATE[$SKIP]}
else
DATE=( $(
for f in ${HDFS[#]}
do
if [[ "$f" > "$value" || "$f" == "$value" ]];then
echo "$f"
fi
done | sort | uniq) )
echo "${DATE[$SKIP]}"
fi
}

Possible to turn 10 similar functions into a for loop?

I am trying to pipe information from my workspaces into another command, and because each workspace contains the same information, with only a different number to identify it, I have written 10 functions where the only difference is one character inside some variable names. I feel like this can be greatly simplified, but I cannot figure out how to get any sort of loop working in my situation.
All here is my script containing the 10 functions:
#!/bin/bash
# Include config file.
. $(dirname $0)/config
getWorkspaceInfo(){
filledWorkspaces=$(i3-msg -t get_workspaces | grep -Po '"'"name"'"\s*:\s*"\K([^"]*)')
currentWorkspace=$(i3-msg -t get_outputs | sed 's/.*"current_workspace":"\([^"]*\)".*/\1/')
}
# Determine the status of each workspace. Current is green, unfocused is white, empty is grey.
workspace1(){
if [[ ${currentWorkspace} -eq 1 ]]; then
workspace1Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "1") == "" ]]; then
workspace1Color=${grey}
else
workspace1Color=${foreground}
fi
echo "%{F${workspace1Color}}${workspace1Name}"
}
workspace2(){
if [[ ${currentWorkspace} -eq 2 ]]; then
workspace2Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "2") == "" ]]; then
workspace2Color=${grey}
else
workspace2Color=${foreground}
fi
echo "%{F${workspace2Color}}${workspace2Name}"
}
workspace3(){
if [[ ${currentWorkspace} -eq 3 ]]; then
workspace3Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "3") == "" ]]; then
workspace3Color=${grey}
else
workspace3Color=${foreground}
fi
echo "%{F${workspace3Color}}${workspace3Name}"
}
workspace4(){
if [[ ${currentWorkspace} -eq 4 ]]; then
workspace4Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "4") == "" ]]; then
workspace4Color=${grey}
else
workspace4Color=${foreground}
fi
echo "%{F${workspace4Color}}${workspace4Name}"
}
workspace5(){
if [[ ${currentWorkspace} -eq 5 ]]; then
workspace5Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "5") == "" ]]; then
workspace5Color=${grey}
else
workspace5Color=${foreground}
fi
echo "%{F${workspace5Color}}${workspace5Name}"
}
workspace6(){
if [[ ${currentWorkspace} -eq 6 ]]; then
workspace6Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "6") == "" ]]; then
workspace6Color=${grey}
else
workspace6Color=${foreground}
fi
echo "%{F${workspace6Color}}${workspace6Name}"
}
workspace7(){
if [[ ${currentWorkspace} -eq 7 ]]; then
workspace7Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "7") == "" ]]; then
workspace7Color=${grey}
else
workspace7Color=${foreground}
fi
echo "%{F${workspace7Color}}${workspace7Name}"
}
workspace8(){
if [[ ${currentWorkspace} -eq 8 ]]; then
workspace8Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "8") == "" ]]; then
workspace8Color=${grey}
else
workspace8Color=${foreground}
fi
echo "%{F${workspace8Color}}${workspace8Name}"
}
workspace9(){
if [[ ${currentWorkspace} -eq 9 ]]; then
workspace9Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "9") == "" ]]; then
workspace9Color=${grey}
else
workspace9Color=${foreground}
fi
echo "%{F${workspace9Color}}${workspace9Name}"
}
workspace10(){
if [[ ${currentWorkspace} -eq 10 ]]; then
workspace10Color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "10") == "" ]]; then
workspace10Color=${grey}
else
workspace10Color=${foreground}
fi
echo "%{F${workspace10Color}}${workspace10Name}"
}
# Pipe functions to the bar infinitely.
while true; do
getWorkspaceInfo
echo "%{c}$(workspace1)${separator}$(workspace2)${separator}$(workspace3)${separator}$(workspace4)${separator}$(workspace5)${separator}$(workspace6)${separator}$(workspace7)${separator}$(workspace8)${separator}$(workspace9)${separator}$(workspace10)"
done | lemonbar -g ${panelWidth}x${panelHeight}+${panelX}+${bottomPanelY} -f "${font}" -f "${iconFont}" -B "${background}" -F "${foreground}" -p -d | \
while true; do read line; eval $line; done &
Here is the config file that I am importing:
#!/bin/bash
# Outside sources
xres="$HOME/.Xresources"
i3config="$HOME/.config/i3/config"
# Fetch information from Xresources
background=$(cat ${xres} | grep -i background | tail -c 8)
foreground=$(cat ${xres} | grep -i foreground | tail -c 8)
black=$(cat ${xres} | grep -i color0 | tail -c 8)
grey=$(cat ${xres} | grep -i color8 | tail -c 8)
red=$(cat ${xres} | grep -i color9 | tail -c 8)
green=$(cat ${xres} | grep -i color10 | tail -c 8)
yellow=$(cat ${xres} | grep -i color11 | tail -c 8)
blue=$(cat ${xres} | grep -i color12 | tail -c 8)
magenta=$(cat ${xres} | grep -i color13 | tail -c 8)
cyan=$(cat ${xres} | grep -i color14 | tail -c 8)
white=$(cat ${xres} | grep -i color15 | tail -c 8)
# Fetch information from i3 config
gapSize=$(cat ${i3config} | grep -i "gaps inner" | awk '{print $3}')
# Workspace names -- independant from i3 config -- workspaces in i3 config should be named numbers 1-10.
workspace1Name="Web Browser"
workspace2Name="Terminal"
workspace3Name="Text Editor"
workspace4Name="Unspecified"
workspace5Name="Unspecified"
workspace6Name="Unspecified"
workspace7Name="Unspecified"
workspace8Name="Unspecified"
workspace9Name="Messenger"
workspace10Name="Music Player"
# Fonts
font="InputSans-10"
iconFont="FontAwesome"
separator="%{F$foreground} |│| "
# Panel size
screenWidth=$(xrandr | grep 'Screen 0'| awk '{print $8}')
screenHeight=$(xrandr | grep 'Screen 0' | awk '{print $10}' | tr -d ",")
panelHeight=$((${gapSize} * 2))
panelWidth=$((${screenWidth} - ${panelHeight}))
panelX=${gapSize}
topPanelY=${gapSize}
bottomPanelY=$((${screenHeight} - ${panelHeight} - ${gapSize}))
Well, the simplest fix is to write something like:
function all_10_workspaces () {
local i
for i in {1..10} ; do
local workspaceNameVar="workspace${i}Name"
local workspaceName="${!workspaceNameVar}"
local color
if (( currentWorkspace == 1 )) ; then
color=$green
elif grep -w -q "$i" <<< "$filledWorkspaces" ; then
color=$foreground
else
color=$grey
fi
echo "%{F$color}$workspaceName"
done
}
. . . however, you should really consider using arrays instead. For example:
workspaceNames=(
'' # 0 (no such workspace)
'Web Browser' # 1
Terminal # 2
'Text Editor' # 3
Unspecified # 4
Unspecified # 5
Unspecified # 6
Unspecified # 7
Unspecified # 8
Messenger # 9
'Music Player' # 10
)
Then, for example, workspace #7 is named "${workspaceNames[7]}", and given a variable i, workspace #i is named "${workspaceNames[i]}".
Something like this, perhaps?
workspaceCount=10
while true; do
# Output will look like "%{c}$(workspace1Color)${separator}$(workspace2Color)${separator}...."
# This is what is sent before the first item in each line
itemSep="%{c}"
for i in {1..$workspaceCount}; do
if [ ${currentWorkspace} -eq $i ]; then
color="${green}"
elif [[ $(echo ${filledWorkspaces} | grep -w "1") == "" ]]; then
color="${grey}"
else
color="${foreground}"
fi
echo -n "${itemSep}${color}"
itemSep="${separator}"
done
echo # Send LF after all items
done
I figured out a way to get what I wanted using ideas from both ruakh and Phil Freed as well as something I came up with on my own. This may not be the shortest or most efficient way to solve the problem, but it is much shorter than having 10 separate functions.
#!/bin/bash
# Include config file.
. $(dirname $0)/config
getWorkspaceInfo(){
filledWorkspaces=$(i3-msg -t get_workspaces | grep -Po '"'"name"'"\s*:\s*"\K([^"]*)')
currentWorkspace=$(i3-msg -t get_outputs | sed 's/.*"current_workspace":"\([^"]*\)".*/\1/')
}
# Determine the status of each workspace. Current is green, unfocused is white, empty is grey.
workspaces(){
workspaces=""
currentSeparator="${separator}"
for i in {1..10} ; do
if [[ ${currentWorkspace} -eq ${i} ]]; then
color=${green}
elif [[ $(echo ${filledWorkspaces} | grep -w "${i}") == "" ]]; then
color=${grey}
else
color=${foreground}
fi
if [[ ${i} -eq 10 ]]; then
currentSeparator=""
fi
workspaces+="%{F$color}${workspaceNames[i]}${currentSeparator}"
done
echo "${workspaces}"
}
# Pipe functions to the bar infinitely.
while true; do
getWorkspaceInfo
echo "%{c}$(workspaces)"
done | lemonbar -g ${panelWidth}x${panelHeight}+${panelX}+${bottomPanelY} -f "${font}" -f "${iconFont}" -B "${background}" -F "${foreground}" -p -d | \
while true; do read line; eval $line; done &
To explain what it does as simply as possible:
Loop through all 10 workspaces, adding what would have been the output of a single function to the end of a new variable. Since I cannot add a separator between each function call anymore, I simply added the separator to the end of the echo, making sure that no separator is added to the last workspace by using a for loop, which sets the separator variable to null.

Where is modification required in this shell script to add the value which is not there in file

I am writing shell script to check "kernel.shmall" value. Purpose of script is if kernel.shmall is less than 4194304 then it should modify the value to 4194304. If kernel.shmall is not there is file /etc/system.conf then it should add the value to file kernel.shmall=4194304
if grep -o "kernel.shmall" /emblocal/sysctl.conf > /dev/null
then
oldvalue=$(grep -v '^#' /emblocal/sysctl.conf|grep kernel.shmall|sed 's/=/ /g'| awk '{ print $2}')
if [ $oldvalue -lt 4194304 ]
then
sed -i "s|\("kernel.shmall" *= *\).*|\14194304|" /emblocal/sysctl.conf
fi
else
echo "kernel.shmall=" >> /emblocal/sysctl.conf
sed -i "s|\("kernel.shmall" *= *\).*|\14194304|" /emblocal/sysctl.conf
fi
script is wokring if value is less than 4194304, but it is not adding "kernel.shmall=4194304" if it is not there. can you help me in this to get it done?
Try this:
#!/bin/sh
oldvalue=$(sed '/^#/d;/kernel\.shmall/!d;s/^[^=]*= *//' /emblocal/sysctl.conf)
if [ "$oldvalue" ]; then
[ $oldvalue -lt 4194304 ] &&
sed -i '/kernel\.shmall/{s/=.*/= 4194304/}' /emblocal/sysctl.conf
else
echo "kernel.shmall = 4194304" >> /emblocal/sysctl.conf
fi
Or more succinctly in AWK:
f=$(mktemp)
awk -vn=kernel.shmall '
function max(a,b) {return a<b?b:a}
{
if ($1 == n) oldval = $3
else print
}
END {print n, "=", max(oldval, 4194304)}
' /emblocal/sysctl.conf > "$f" && cp "$f" /emblocal/sysctl.conf
rm "$f"

Linux bash script -

I am trying to use whether or not a line contains a date as a condition for an if statement:
if [grep -n -v '[0-9][0-9][0-9][0-9]' $line |wc -l==0]
then
...
The above returns an error. I don't necessarily need to use grep. The line processed by grep would look like:
1984 Dan Marino QB Miami Dolphins
Any help is appreciated.
if [[ $(echo $line | grep -q '[0-9][0-9][0-9][0-9]') ]]; then
# do something
fi
You can check this using bash built-ins:
re='\b[[:digit:]]{4}\b'
if [[ $line =~ $re ]] ; then
echo ok;
fi
[grep -n -v '[0-9][0-9][0-9][0-9]' $line |wc -l==0]
problem 1: [(space).....(space)] you need those spaces
problem 2: there is no [ foo==bar ] you can do something like [ $(echo "0") = "0" ] or [[ $(echo "0") == 0 ]] here the $(echo "0") is an example, you should fill with your commands.
You can just call grep with -q option and check the return value:
if [ $(grep -qv '[0-9][0-9][0-9][0-9]' $line) -eq 0 ]; then
# ...
fi
Use command substitution and proper bash syntax.
[[ "`grep -n -v '[0-9][0-9][0-9][0-9]' $line | wc -l`" -eq 0 ]]

Resources