i want do something like that:
$ ( foo1.sh $1 | foo2.sh | bar.sh |) arg
sometime pipe is fast than write function, but i can pass parameters/args between pipe
now: I'm triad to search for package firefox by xbps but i get this error bellow:
$ xbps-query -Rs $1 | fzf firefox
xbps-query: option requires an argument -- 's'
unknown option: firefox
Pipelines don't have arguments. Instead, write a script or function that takes arguments and runs the pipeline you want:
foo() {
xbps-query -Rs "$1" | fzf
}
You can then run it with foo firefox
Related
This question already has answers here:
Pass command-line arguments to grep as search patterns and print lines which match them all
(3 answers)
Match two strings in one line with grep
(23 answers)
Closed 11 months ago.
I have a datafile as below.
datafile
Sterling|Man City|England
Kane|Tottenham|England
Ronaldo|Man Utd|Portugal
Ederson|Man City|Brazil
If I want to find a player that has a "Man City" and "England" trait I would write a command as such in the interpreter.
grep "Man City" datafile | grep "England"
output: Sterling|Man City|England
Like this, I want to make a shell script that receives multiple(more than 1) arguments that finds a line which has all the arguments in it. It would receive input as arg1 arg2 arg3... and return all the data lines that has the arguments. The program would run like this.
$ ./find ManCity England
output: Sterling|Man City|England
But I have absolutely no idea how to implement the command that does multiple grep for each argument. Can you use a for loop and implement in a code that does something like this?
grep arg1 datafile | grep arg2 | grep arg3 ...
Also, if there is a better way to find a data line that has all the arguments(AND of the arguments) can you please show me how?
You need this:
#!/bin/bash
myArray=( "$#" ) # store all parameters as an array
grep="grep ${myArray[0]} somefile" # use the first param as the first grep
unset myArray[0]; # then unset the first param
for arg in "${myArray[#]}"; do
grep="$grep | grep '$arg'" # cycle through the rest of the params to build the AND grep logic
done
eval "$grep" # and finally execute the built line
You can use the awk command to do so. Awk is a very useful command and also supports loops.
Below is the awk command to find the AND of multiple patterns:
> awk '/Man City/ && /England/' test.txt
Sterling|Man City|England
Incase you want to do OR:
> awk '/Man City/ || /England/' test.txt
Sterling|Man City|England
Kane|Tottenham|England
Ederson|Man City|Brazil
Reference
I'm Brand new to Rust and I have been writing some practice apps. I am trying to accept command-line arguments using Clap. The code below takes a string and a number and prints them back out, as such:
$ cargo run "this is a test" -n11
this is a test
11
This works fine, but I want to be able to pipe input in place of the string like this:
$ echo "this is a test" | cargo run -- -n11
this is a test
11
Trying this yields:
error: The following required arguments were not provided:
<INPUT>
USAGE:
clap_sample --num <num> <INPUT>
For more information try --help
I can get around this using xargs like this:
$ echo "this is a test" | xargs -d '\n' cargo run -- -n11
Is there a better way to do this so that I can accept piped in strings while still using the -n option? Thanks in advance.
use clap::{Arg, App};
fn main() {
let matches = App::new("Clap Sample")
.arg(Arg::new("INPUT")
.required(true)
.index(1))
.arg(Arg::new("num")
.short('n')
.long("num")
.takes_value(true))
.get_matches();
let usr_string = matches.value_of("INPUT").unwrap();
let key: u8 = matches.value_of("num").unwrap()
.parse()
.expect("NaN :(");
println!("{}", usr_string);
println!("{}", key);
}
Bonus question:
If I pipe a string in with xargs, I can have newlines in the string (with delimiter set to \0) and they are reflected in the output. If I pass it directly without echo and xargs, a literal '\n' shows in the output. Is there a way to have the newline represented when run directly?
Your code is checking the command line for arguments, it is not reading standard input. To use xargs get move the input from the pipe to the command line is a good way of doing it.
echo -n "this is a test" | xargs cargo run -- -n11
The other option you have is to change your program so it reads from stdin if no user_string argument was given. Here is a good starting point to read stdin https://doc.rust-lang.org/std/io/struct.Stdin.html
You should also replace the unwrap() here:
let key: u8 = matches.value_of("num").unwrap()
with a check if the argument was given as it was not .required(true) for instance with a
if let Some(key) = matches.value_of("num")
or maybe with a unwrap_or("0")
So I'm getting to get the CPU core temperature using sensors command.
Inside conky, I wrote
$Core 0 Temp:$alignr${execi 1 sensors | grep 'Core 0' | awk {'print $3'}} $alignr${execibar 1 sensors | grep 'Core 0' | awk {'print $3'}}
Each second I'm running the exact same command sensors | grep 'Core 0' | awk {'print $3'} in two places for exact same output. Is there is a way to hold the output inside a variable and use that variable in place of the commands.
conky does not have user variables. What you can do instead is call lua from conky to do this for you. The lua language is usually built-in to conky by default, so you need only put some code in a file, include the file in the conky setup file, and call the function. For example, these shell commands will create a test:
cat >/tmp/.conkyrc <<\!
conky.config = {
lua_load = '/tmp/myfunction.lua',
minimum_height = 400,
minimum_width = 600,
use_xft = true,
font = 'Times:size=20',
};
conky.text = [[
set ${lua myfunction t ${execi 1 sensors | awk '/^Core 0/{print 0+$3}'}}°C
get ${lua myfunction t}°C ${lua_bar myfunction t}
]]
!
cat >/tmp/myfunction.lua <<\!
vars = {}
function conky_myfunction(varname, arg)
if(arg~=nil)then vars[varname] = conky_parse(arg) end
return vars[varname]
end
!
conky -c /tmp/.conkyrc -o
In the myfunction.lua file, we declare a function myfunction() (which needs to be prefixed conky_ so we can call it from conky). It takes 2 parameters, the name of a variable, and a conky expression. It calls conky_parse() to evaluate the expression, and saves the value in a table vars, under the name provided by the caller. It then returns the resulting value to the caller. If no expression was given, it will return the previous value.
In the conky.text the line beginning set calls myfunction in lua with the arbitrary name of a variable, t, and the execi sensors expression to evaluate, save, and return. The line beginning get calls myfunction to just get the value.
lua_bar is similar to exec_bar, but calls a lua function, see man conky. However, it expects a number without the leading + that exec_bar accepts, so I've changed the awk to return this, and have added the °C to the conky text instead.
I'm trying to use "sort -V" command (aka version-sort) in a sh file.
Specifically, I have the following line of code in a sh file:
SOME_PATH="$(ls dir_1/dir_2/v*/filename.txt | sort -V | tail -n1)"
What I'm trying to accomplish through the above command is that given a list of file paths with different version numbers, I want to get the file path with the greatest version number.
For example, let's assume that I have the following list of file paths:
dir_1/dir_2/v1/filename.txt,
dir_1/dir_2/v2/filename.txt,
dir_1/dir_2/v11/filename.txt
Then, I want the command to return dir_1/dir_2/v11/filename.txt instead of dir_1/dir_2/v2/filename.txt since the former has the greatest version value, "11".
From my understanding the above linux command precisely accomplishes this.
I confirmed it working on the Linux bash terminal.
However, when I run a sh file with the above command in it, I'm getting a
"ERROR: Unknown command line flag 'V'" error message.
Is there a way to make version-sort work in a sh file?
If not, is there a way to implement it not using -V flag?
Thank you.
Using shell's printf and awk:
SOME_PATH=$(printf %s\\0 dir_1/dir_2/v*/filename.txt |
awk 'BEGIN{FS="/";RS="\0";v=0}{match($3,/v([[:digit:]]+)/,m);if(m[1]>v){v=m[1];l=$0}}END{print l}')
Using awk only:
SOME_PATH=$(awk 'BEGIN{delete ARGV[0];v=0;for(i in ARGV){split(ARGV[i],s,"/");match(s[3],/v([[:digit:]]+)/,m);if(m[1]>v){v=m[1];l=ARGV[i]}}}END{print l}' dir_1/dir_2/v*/filename.txt)
Formatted awk script:
#!/usr/bin/env -S awk -f
BEGIN {
delete ARGV[0]
v=0
for (i in ARGV) {
split(ARGV[i], s, "/")
match(s[3], /v([[:digit:]]+)/, m)
if (m[1]>v) {
v=m[1]
l=ARGV[i]
}
}
}
END {
print l
}
Using a null delimited list stream, and not parsing the output of ls 1:
SOME_PATH=$(
printf '%s\0' dir_1/dir_2/v*/filename.txt |
sort -z -t'/' -k3V |
tail -zn1 |
tr -d '\0'
)
How it works:
printf '%s\0' dir_1/dir_2/v*/filename.txt: Expands the paths into a null delimited stream output.
sort -z -t'/' -k3V: Sorts the null delimited input stream on -k3V version number from the 3rd column, -t'/' using / as a delimiter.
tail -zn1: Outputs the least null delimited entry from the input stream.
tr -d '\0': Trim-out any remaining null to prevent the shell from complaining with error: warning: command substitution: ignored null byte in input.
StackExchange: Why not parse ls (and what to do instead)?
I am writing a bash shell script to output the suffixes of filenames.
In this case I use:
sed 's|.*\.||'
So the output is e.g.:
png
exe
c
But what do I do if the file name has no suffix and therefore no dot? My output should be "no suffix", but I don't know how to do this with sed.
EDIT
What I've already tried:
Directory:
abc.x
abc.y
abc
Input:
find . -type f | sed -E 's/^[^.]+$/no suffix/; s/.*\.//'
Output:
x
y
/abc
Use 2 consecutive substitutions:
sed -E 's/^[^.]+$/no suffix/; s/.+\.//'
One in awk. First some test material that was not provided:
$ cat foo
this.foo
that.bar
nothing
The awk:
$ awk '{n=split($0,a,".");print (n>1?a[n]:"no suffix")}' foo
foo
bar
no suffix
$ cat file
abc.x
abc.y
abc
$ awk -F'.' '{print (NF>1 ? $NF : "no suffix")}' file
x
y
no suffix
How about
sed '/.*\./s///;t;s/.*/no suffix/'
The regex matches lines with a dot. On those lines, we perform a substitution. If a substitution occurred, we are done. Otherwise, perform the other substitution.
The use of an empty regex in the substitution pattern uses the previous pattern. The t command branches if a substitution occurred; without an argument, we branch to the end of the script. (Otherwise, you can set a label with :label and branch to that with tlabel.)
You can accomplish the same with the POSIX shell parameter expansions without invoking separate utilities. For example, to test whether a file contains a '.' you can simply use test, e.g.:
[ "$i" = "${i%.*}" ]
See Posix Programmer's Manual - Shell Command Language - Parameter Expansion
If it tests TRUE, then no extension is present, otherwise, you can use an additional parameter expansion to obtain the extension itself, e.g.
[ "$i" = "${i%.*}" ] && echo "$i - no suffix" || echo "$i - ${i##*.}"
(note: you would need an additional test to exclude .foo (e.g. dotfiles), but that is left to you)
Wrap that in a loop and exclude directory files and you can test every file within a directory or use read within a loop and pipe a list of names to it. For example, looping over the files in a directory would results in:
...
ftlcdfil.c - c
geany-plugin_build_w_gtk+2.patch - patch
geany-plugins.spec - spec
geany.spec - spec
geany_build_w_gtk+2.patch - patch
getfl - no suffix
gtkw_save_test.txt - txt
gtkwrite-master.zip - zip
helloleap - no suffix
helloleap.c - c
jnl - no suffix
messages - no suffix
opensuse_15.0_1s_delay.svg - svg
...
Using Perl
/tmp> ls ab*
abc abc.x abc.y
/tmp> perl -e ' print /\./? "$_\n" : "no suffix\n" for(glob("ab*")) '
no suffix
abc.x
abc.y
/tmp>