Why is my awk print not showing up on the terminal - bash

I have the following script which does a "which -a" on a command then a "ls -l" to let me know if it's a link or not .. ie "grep" since I have gnu commands installed (Mac with iTerm).
#!/usr/bin/env bash
which -a $1 | xargs -I{} ls -l "{}" \
| awk '{for (i = 1; i < 9; i++) $i = ""; sub(/^ */, ""); print}'
When I run this from the script "test grep" I receive no output, but when I run it via "bash -x test grep" I receive the following:
bash -x test grep
+ which -a grep
+ xargs '-I{}' ls -l '{}'
+ awk '{for (i = 1; i < 9; i++) $i = ""; sub(/^ */, ""); print}'
/usr/local/bin/grep -> ../Cellar/grep/3.1/bin/grep
/usr/bin/grep
The last 2 lines is what I'm looking to display. Thought this would be easier to do ;-) .. I also tried appending the pipe thinking printf would fix the issue:
| while read path
do
printf "%s\n" "$path"
done
Thanks and .. Is there a better way to get what I need?

The problem is that you named your script test.
If you want to run a command that's not in your PATH, you need to specify the directory it's in, e.g. ./test.
You're not getting an error for trying to run test because there is a built-in bash command called test that is used instead. For extra confusion, the standard test produces no output.
In conclusion:
Use ./ to run scripts in the current directory.
Never call your test programs test.

Thanks for the never naming a script "test" .. old habits are hard to break (I came from a non-unix background.
I ended with the following
for i in $(which -a $1)
do
stat $i | awk NR==1{'$1 = ""; sub(/^ */, ""); print}'
done
or simpler
for i in $(which -a $1)
do
stat -c %N "$i"
done

Consider the following shell function:
cmdsrc() {
local cmd_file cmd_file_realpath
case $(type -t -- "$1") in
file) cmd_file=$(type -P -- "$1")
if [[ -L "$cmd_file" ]]; then
echo "$cmd_file is a symlink" >&2
elif [[ -f "$cmd_file" ]]; then
echo "$cmd_file is a regular file" >&2
else
echo "$cmd_file is not a symlink or a regular file" >&2
fi
cmd_file_realpath=$(readlink -- "$cmd_file") || return
if [[ $cmd_file_realpath != "$cmd_file" ]]; then
echo "...the real location of the executable is $cmd_file_realpath" >&2
fi
;;
*) echo "$1 is not a file at all: $(type -- "$1")" >&2 ;;
esac
}
...used as such:
$ cmdsrc apt
/usr/bin/apt is a symlink
...the real location of the executable is /System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/apt
$ cmdsrc ls
/bin/ls is a regular file
$ cmdsrc alias
alias is not a file at all: alias is a shell builtin

Took some suggestions and came up with the following:
The prt-underline is just a fancy printf function. I decided not to go with readline since the ultimate command resolution may be unfamiliar to me and I only deal with regular files .. so does't handle every situation but in the end gives me the output I was looking for. Thanks for all the help.
llt ()
{
case $(type -t -- "$1") in
function)
prt-underline "Function";
declare -f "$1"
;;
alias)
prt-underline "Alias";
alias "$1" | awk '{sub(/^alias /, ""); print}'
;;
keyword)
prt-underline "Reserved Keyword"
;;
builtin)
prt-underline "Builtin Command"
;;
*)
;;
esac;
which "$1" &> /dev/null;
if [[ $? = 0 ]]; then
prt-underline "File";
for i in $(which -a $1);
do
stat "$i" | awk 'NR==1{sub(/^ File: /, ""); print}';
done;
fi
}

Related

bash optional command in variable

i have a code:
L12(){
echo -e "/tftpboot/log/archive/L12/*/*$sn*L12*.log /tftpboot/log/diag/*$sn*L12*.log"
command="| grep -v hdd"
}
getlog(){
echo $(ls -ltr $(${1}) 2>/dev/null `${command}` | tail -1)
}
however $command does not seem to be inserting | grep -v hdd correctly
i need $command to be either empty or | grep
is there a simple solution to my issue or should i go for different approach
edit:
there may be another problem in there
i am loading a few "modules"
EVAL.sh
ev(){
case "${1}" in
*FAIL*) paint $red "FAIL";;
*PASS*) paint $green "PASS";;
*)echo;;
esac
result=${1}
}
rackinfo.sh (the "main script")
#! /bin/bash
#set -x
n=0
for src in $(ls modules/)
do
source modules/$src && ((n++))
## debugging
# source src/$src || ((n++)) || echo "there may be an issue in $src"
done
## debugging
# x=($n - $(ls | grep src | wc -l))
# echo -e "$x plugin(s) failed to laod correctly"
# echo -e "loaded $n modules"
########################################################################
command=cat
tests=("L12" "AL" "BI" "L12-3")
while read sn
do
paint $blue "$sn\t"
for test in ${tests[#]}
do
log="$(ev "$(getlog ${test})")"
if [[ -z ${log} ]]
then
paint $cyan "${test} "; paint $red "!LOG "
else
paint $cyan "${test} ";echo -ne "$log "
fi
done
echo
done <$1
the results i get are still containing "hdd" for L12()
Set command to cat as a default.
Also, it's best to use an array for commands with arguments, in case any of the arguments is multiple words.
There's rarely a reason to write echo $(command). That's essentially the same as just writing command.
#default command does nothing
command=(cat)
L12(){
echo -e "/tftpboot/log/archive/L12/*/*$sn*L12*.log /tftpboot/log/diag/*$sn*L12*.log"
command=(grep -v hdd)
}
getlog(){
ls -ltr $(${1}) 2>/dev/null | "${command[#]}" | tail -1)
}

Git bash doesn't recognize 'rev' command

I am trying to use a function that shortens my bash prompt. I have added it in .bash_profile:
function last_two_dirs {
pwd |rev| awk -F / '{print $1,$2}' | rev | sed s_\ _/_
}
export PS1='$(last_two_dirs) $(__git_ps1) ➡ '
But I get an error bash: rev: command not found everytime I launch git bash.
I have set the PATH correctly since other commands work correctly except rev. Is rev not part of git bash? Or is there any other way to show only the parent and the current directory for the bash prompt?
OS: Windows 10
Your environment doesn't seem to have the rev command. However, you don't need it, there are built-in facilities for what you want to do.
To get the current working directory in your PS1, use \w:
PS1='\w\$ '
This gets you the full path, so your prompt will look something like
~/tinker/so/subdir/subsubdir$
Now, set the $PROMPT_DIRTRIM variable to the number of trailing directories to retain:
PROMPT_DIRTRIM=2
This will get you a prompt like
~/.../subdir/subsubdir$
This is the code that worked for me -
PROMPT_COMMAND='case $PWD in
$HOME) HPWD="~";;
$HOME/*/*) HPWD="${PWD#"${PWD%/*/*}/"}";;
$HOME/*) HPWD="~/${PWD##*/}";;
/*/*/*) HPWD="${PWD#"${PWD%/*/*}/"}";;
*) HPWD="$PWD";;
esac'
PS1='$HPWD \$'
referred from link
bash function around $PWD
Under bash, there are lot of trick and features you could use to make this a lot quicker and efficient
Simply two last path level:
last_two_dirs() {
local left=${PWD%/*};
echo "${PWD#${left%/*}/}"
}
More complex: first and last level:
path_parts() {
local APATH
IFS=/ read -a APATH <<<"$PWD"
if ((${#APATH[#]}>3)) ;then
echo "/${APATH[1]}..${APATH[-1]}"
else
echo "$PWD"
fi
}
Another special case
Les imagine your path in this kind:
/srv/dist/invoices-2019/data-2019-02-10/seq-123
To trim all part of path until first dash:
path_dash_Trim () {
local APATH;
IFS=/ read -a APATH <<< "$PWD";
APATH="${APATH[*]#*-}";
echo "${APATH// /\/}"
}
will render
/srv/dist/2019/2019-02-10/123
Fork performance issue
In order to reduce performance issue, there is a proper way to eliminate forks ( var=$(commnand) ): Set variable in function:
Simply replace
echo ${PWD#${left%/*}/}
by
printf -v $1 %s "${PWD#${left%/*}/}"
or better:
printf -v ${1:-myResult} '%s' "${PWD#${left%/*}/}"
Sample:
last_two_dirs() { local left=${PWD%/*};printf -v $1 "%s" "${PWD#${left%/*}/}" ;}
last_two_dirs result
printf -v $1 %s "$result"
or
path_parts() {
local APATH
IFS=/ read -a APATH <<<"$PWD"
if ((${#APATH[#]}>3)) ;then
printf -v $1 %s "/${APATH[1]}..${APATH[-1]}"
else
printf -v $1 %s "$PWD"
fi
}
and so on...
Then
export -f path_parts
PROMPT_COMMAND='path_parts PS1&&PS1+=" \\$ "'
or even simply dedicated function:
myPrompt() {
local APATH fmt='%s\[\e];%s\a\] $ '
IFS=/ read -a APATH <<<"$PWD"
if ((${#APATH[#]}>4)) ;then
printf -v PS1 "$fmt" "/${APATH[1]}..${APATH[-2]}/${APATH[-1]}"{,}
else
printf -v PS1 "$fmt" "$PWD"{,}
fi
}
PROMPT_COMMAND=myPrompt
Install MSYS2 tools, it has the tools you need.
function last_two_dirs ()
{
awk -F/ '{print ((NF>1)?$(NF-1)"/":"")""$NF}' <<< $PWD
}
You can simply use
pwd | sed -r 's|.*/([^/]+/[^/]+)$|\1|'
instead

Why grep is not getting last argument?

This is a script that searches each line of a file ($1) into another file ($2):
val=$(wc -l < $1)
for ((i = 1; i <= val; i++))
do
line=$(sed '$i!d' $1)
if grep -q "$(echo $line)" $2
then
echo found
fi
done
But it gets stuck in the if grep.
It behaves as if it's not getting the $2.
a script that searches each line of a file ($1) into another file ($2)
No need to write your own script for that. Use grep's -f option:
if grep -qf "$1" "$2"; then
echo found
else
echo not found
fi
Solved, the problem was in how I was passing the line number in sed:
#!/bin/bash
val=$(wc -l < $1)
for ((i = 1; i <= val; i++))
do
line=$(sed "$i!d" $1)
if ! grep -q "$(echo $line)" $2
then
echo $line
fi
done
This works fine, if you do:
./script file1 file2
It gives you the lines of the first file that are missing in the second.

how to put a filename from the newest file in the directory into a variable

I am making a script. i need to automate a portion that looks at all the files in the directory containing the string "HNAZXLCOM" in the name then take the newest file with that string in the name and put the filename into a variable.
Thanks
function latest {
if [[ $FUNCNAME == ${FUNCNAME[1]} ]]; then
unset -v x latest files
printf -v "$#"
elif (($# > 2)); then
printf '%s\n' "Usage: $FUNCNAME <glob> <varname>" 'Error: Takes at most 2 arguments. Glob defaults to *'
return 1
else
if ! shopt -q nullglob; then
typeset -f +t "$FUNCNAME"
trap 'shopt -u nullglob; trap - RETURN' RETURN
shopt -s nullglob
fi
IFS= typeset -a 'files=(${1:-*})'
typeset latest x
for x in "${files[#]}"; do
[[ -d $x || $x -ot $latest ]] || latest=$x
done
${2:+"$FUNCNAME"} "${2:-printf}" -- %s "$latest"
fi
}
latest '*HNAZXLCOM*' myVar
If I read you right
var="$(ls -t1 *HNAZXLCOM*|head -n 1)"
From man ls, the -t switch is relevant. Also see the -1 switch.
-t sort by modification time, newest first
-1 list one file per line
The -1 switch is not relevant, because ls automatically writes only one line per name when outputting to a pipe.
At a command prompt, the following function will list the 13 most recent files; eg, xt *HNAZXLCOM* would list the 13 most recent files with that string in their names, while xt by itself would list the most recent files (of any pattern) in the current working directory. Adjust the number to whatever you prefer. Some versions of head may require -n 13 instead of -13. In your application,
H=$(ls -t *HNAZXLCOM* | head -n 1)
is appropriate.
xt ()
{
date;
ls --color -Glt --color --time-style="+%Y-%m-%d %T" $* | grep -v "/" | head -13
}

Simulating the find command: why is my code not recursing correctly?

My assignment is to write a Unix shell script that asks the user for the name of a directory, and then works exactly like find.
Here is what I have so far:
#!/bin/bash
dir_lister()
{
cd "$1"
echo "$1"
list=$(ls -l ${1})
nolines=$(echo "$list" | awk 'END{printf "%d",NF}')
if [ $nolines -eq 2 ]
then
echo "$1"
return
fi
filelist=$(echo "$list" | grep ^-.*)
dirlist=$(echo "$list" | grep ^d.*)
filename=$(echo "$filelist"| awk '{printf "%s\n",$NF}')
present=$(pwd)
echo "$filename"| awk -v pres=$present '{printf "%s/%s\n",pres,$0}'
dirlist2=$(echo "$dirlist" | awk '{printf "%s\n",$NF}')
echo "$dirlist2" | while IFS= read -r line;
do
nextCall=$(echo "$present/$line");
dir_lister $nextCall;
cd ".."
done
cd ".."
}
read -p "Enter the name of the direcotry: " dName
dir_lister $dName
The problem is, after a depth of three directories, this script gets into an infinite loop, and I don't see why.
EDIT:
Here is the code i came up with after looking at your answer, it still doesn't go more than 1 directory depth:
#!/bin/bash
shopt -s dotglob # don't miss "hidden files"
shopt -s nullglob # don't fail on empty directories
list_directory()
{
cd "$2"
cd "$1"
##echo -e "I am called \t $1 \t $2"
for fileName in "$1/"*
do
##echo -e "hello \t $fileName"
if [ -d "$fileName" ];
then
echo "$fileName"
list_directory $fileName $2
else
echo "$fileName"
fi
done
}
read -p "Enter the direcotory Name: " dirName
var=$(pwd)
list_directory $dirName $var
Okay, that is completely the wrong way to list files in a directory (see ParsingLs). I'll give you the pieces and you should be able to put them together into a working script.
Put this at the top of your script:
shopt -s dotglob # don't miss "hidden files"
shopt -s nullglob # don't fail on empty directories
Then you can easily loop over directory contents with:
for file in "$directory/"* ; do
#...
done
Test if you have a directory:
if [ -d "$file" ] ; then
# "$file" is a directory, recurse...
fi

Resources