Pipe commands in bash function - bash

When you define a bash function you can call bash commands with command command.
function ls() {
clear
command ls "$#"
}
How would you pipe commands in bash function?
e.g.
function ls() {
clear
command ls "$#" | head
}
EDIT: The output would be OK, but there is --color=auto. Look here

Try this in your ~/.bashrc
function ls() { clear ; builtin ls "$#" | head ; }
It's similar to the function you have already but with the inclusion of builtin, it guarantees not to get stuck in a loop calling itself. Hope this works!
EDIT: It should be noted that any colour information produced by ls with the --color=auto option won't be carried through the pipe to head.

You can pipe the colour information generated by the ls command to head if you run ls in a so-called pseudo terminal (so that ls thinks it is writing its output to a terminal, and not a pipe). This can be achieved by using the script command.
ls() {
type -P command 1>/dev/null ||
{ echo 'No "command" executable found!'; return 1; }
clear
script -q /dev/null command ls -G "$#" | tr -d '\r' | head
}
cat /usr/bin/command # on Mac OS X 10.6.8
#!/bin/sh
# $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $
# This file is in the public domain.
builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$#"}
For more information see: ls command operating differently depending on recipient

Related

How to source bash script in zsh

I have bash script which heavily uses bash features and does not run in zsh, dash, and sh.
My prefered shell is zsh. How can I source this bash script into zsh shell ?
I tried
function in() {
bash -c "'$# ; exec zsh'"
}
but this gives me syntax errors like this
❯ in source t.bash
t.bash ; exec zsh': -c: line 1: unexpected EOF while looking for matching `''
t.bash ; exec zsh': -c: line 2: syntax error: unexpected end of file
I also tried to use xargs but then enviroment variables set by my bash script are not set in zsh
function in () {
echo -c "'$# ; exec zsh'" | xargs -t bash
}
but then enviroment variables set by my bash script are not set in zsh
copy pasting the command shown by xargs -t does work
You said "copy pasting the command shown by xargs -t does work".
So ... if you changed
function in () {
echo -c "'$# ; exec zsh'" | xargs -t bash
}
to
function in () {
echo -c "'$# ; exec zsh'" | xargs -t bash >"${BATCH_FILE}"
chmod 754 "${BATCH_FILE}" ; "${BATCH_FILE}"
}
wouldn't that give you what you need?

Place command output in shell for further editing

I found a nice zsh command online that uses fzf to fuzzy-search shell history:
fh() {
eval $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s --tac | sed 's/ *[0-9]* *//')
}
However, instead of eval-ing the command, I'd like to place it in my prompt and edit it further before running it, something like:
$ fh grep
(search happens)
$ grep -RI foo . <--- cursor
I tried replacing eval with echo, which is better but doesn't give me the command to edit. Is there a way to do this in bash/zsh?
Here is a related thread with a suggestion to use xvkbd, but I was hoping there is something simpler.
In zsh, the vared builtin for the zsh line editor (ZLE) will let you edit an environment variable.
After editing, the updated variable can be used to execute the command:
fh() {
fzfresult=$( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s --tac | sed 's/ *[0-9]* *//')
vared -p 'fzfout> ' -ac fzfresult
${fzfresult[#]}
}
In the vared command, -p sets the prompt. The -ac option changes the fzfresult variable to an array so we can execute it in the next step.
I don't have fzf installed, so this isn't completely tested, but the result should look like this:
% fh grep
fzfout> grep -RI foo . <-- edit, hit enter, get output:
file1: text with foo
file4: more text with foobar
I ended up using this:
fh() {
print -s $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s --tac | sed 's/ *[0-9]* *//')
}
ie silently print, followed by up-arrow, which brings up the command for editing. Would love to hear if there is a simpler way.

Set a command to a variable in bash script problem

Trying to run a command as a variable but I am getting strange results
Expected result "1" :
grep -i nosuid /etc/fstab | grep -iq nfs
echo $?
1
Unexpected result as a variable command:
cmd="grep -i nosuid /etc/fstab | grep -iq nfs"
$cmd
echo $?
0
It seems it returns 0 as the command was correct not actual outcome. How to do this better ?
You can only execute exactly one command stored in a variable. The pipe is passed as an argument to the first grep.
Example
$ printArgs() { printf %s\\n "$#"; }
# Two commands. The 1st command has parameters "a" and "b".
# The 2nd command prints stdin from the first command.
$ printArgs a b | cat
a
b
$ cmd='printArgs a b | cat'
# Only one command with parameters "a", "b", "|", and "cat".
$ $cmd
a
b
|
cat
How to do this better?
Don't execute the command using variables.
Use a function.
$ cmd() { grep -i nosuid /etc/fstab | grep -iq nfs; }
$ cmd
$ echo $?
1
Solution to the actual problem
I see three options to your actual problem:
Use a DEBUG trap and the BASH_COMMAND variable inside the trap.
Enable bash's history feature for your script and use the hist command.
Use a function which takes a command string and executes it using eval.
Regarding your comment on the last approach: You only need one function. Something like
execAndLog() {
description="$1"
shift
if eval "$*"; then
info="PASSED: $description: $*"
passed+=("${FUNCNAME[1]}")
else
info="FAILED: $description: $*"
failed+=("${FUNCNAME[1]}")
done
}
You can use this function as follows
execAndLog 'Scanned system' 'grep -i nfs /etc/fstab | grep -iq noexec'
The first argument is the description for the log, the remaining arguments are the command to be executed.
using bash -x or set -x will allow you to see what bash executes:
> cmd="grep -i nosuid /etc/fstab | grep -iq nfs"
> set -x
> $cmd
+ grep -i nosuid /etc/fstab '|' grep -iq nfs
as you can see your pipe | is passed as an argument to the first grep command.

Bash function argument parsing

I want to have a function I can call from the command line that takes the following:
$ command_name /some/path/file.java
and turns into the following call:
command /some/path:file
So basically the part I'm having trouble with is substituting a : for the last / and stripping the file extension.
It's not 100% clear what you question is. Do you want a bash function or a bash script? Splitting paths and files is easily done with the commands basename and dirname.
e.g.:
$ dirname /path/to/file.txt
/path/to
$ basename /path/to/file.txt
file.txt
But if you must do it with a regex, sed works well:
$ echo /path/to/file.txt | sed "s/.*\///"
file.txt
$ echo /path/to/file.txt | sed -r "s/(.+)\/.+/\1/"
/path/to
First a script:
#! /usr/bin/env bash
COMMAND="/bin/echo"
JAVA="$1"
path=`dirname "$JAVA"`
file=`basename "$JAVA"`
exec "$COMMAND" "$path:$file"
And now a function:
fnA()
{
COMMAND="$1"
JAVA="$2"
path=`dirname "$JAVA"`
file=`basename "$JAVA"`
exec "$COMMAND" "$path:$file"
}
Assuming that the original path string,
/some/path/file.java
is passed to your script as $1, you get the modified string as
path_string_with_colon=$(dirname $1):$(basename $1 .java)

Executing code in bash only if a string is not found in a file

I'm trying to execute a block of code only if the string SVN_BRANCH is not found in /etc/profile. My current code looks like the following:
a = cat /etc/profile
b = `$a | grep 'SVN_BRANCH'`
not_if "$b"
{
....
...
...
}
This fails as given. How should it be done?
grep can take file as an argument, you don't need to cat the file and then pass the content to grep with pipe, that's totally unnecessary.
This is an example of if else block with grep:
if grep -q "pattern" filepath;then
echo "do something"
else
echo "do something else"
fi
Note:
-q option is for quite operation. It will hide the output of grep command (error will be printed).
If you want it to not print any errors too then use this:
if grep -sq "pattern" filepath;then
Or this:
if grep "pattern" filepath >/dev/null 2>&1;then
>/dev/null is to redirect the output to /dev/null
2>&1 redirects both stderr and stdout
you can use the exit code of the grep command to determine whether to execute your code block like this
cat /etc/profile | grep SVN_BRANCH 2>&1 >/dev/null || {
....
....
}

Resources