Perl ack tool with output parameter and new lines - ack

I use ack very often, but something I have not been able to discover is how to make it add new lines when using the output parameter.
Example
echo 'name=Joe,id=123' | ack '^name=(\w+),id=(\d+)' --output="User name: $1\nUser ID: $2"
I expect
User name: Joe
User ID: 123
but I get
User name: Joe\nUser ID: 123
Anyway to make ack respect the new lines characters in the output?
Thanks

You are passing \n to ack instead of a newline. Replace
--output='User name: $1\nUser ID: $2'
with
--output='User name: $1
User ID: $2'
or
--output="$( printf 'User name: $1\nUser ID: $2' )"
or
--output=$'User name: $1\nUser ID: $2' # bash

You can put arbitrary code into that string via #{[ … ]} interpolation. For some reason a literal "\n" string won't work, but chr 0x0A or $/ does.
Example:
$ echo a b | ack '(\w+)\s+(\w+)' --output '$1#{[ chr 0x0A ]}$2'
Output:
a
b
Note that this kind of functionality is likely to break in the future, see also discussions like https://github.com/beyondgrep/ack2/issues/531

Related

How do I get just the STDOUT of a salt state?

my output now
I'm learning salt stack right now and I was wondering if there was a way to get the stdout of a salt state and put it into a document and then send it to the master. Or is there a better way to do this?
To achieve this, we'll have to save the execution of the script in a variable. It will contain a hash containing keys that are showing up under changes:. Then the contents of this variable (stdout) can be written to a file.
{% set script_res = salt['cmd.script']('salt://test.sh') %}
create-stdout-file:
file.managed:
- name: /tmp/script-stdout.txt
- contents: {{ script_res.stdout }}
The output is already going to the master. It would be better to actually output in json and query down to the data you want in your document on the master.
such as the following
Normal output
$ sudo salt salt00\* state.apply tests.test3
salt00.wolfnet.bad4.us:
----------
ID: test_run
Function: cmd.run
Name: echo test
Result: True
Comment: Command "echo test" run
Started: 10:39:51.103057
Duration: 18.281 ms
Changes:
----------
pid:
8661
retcode:
0
stderr:
stdout:
test
Summary for salt00.wolfnet.bad4.us
------------
Succeeded: 1 (changed=1)
Failed: 0
------------
Total states run: 1
Total run time: 18.281 ms
json output
$ sudo salt salt00\* state.apply tests.test3 --out json
{
"salt00.wolfnet.bad4.us": {
"cmd_|-test_run_|-echo test_|-run": {
"name": "echo test",
"changes": {
"pid": 9057,
"retcode": 0,
"stdout": "test",
"stderr": ""
},
"result": true,
"comment": "Command \"echo test\" run",
"__sls__": "tests.test3",
"__run_num__": 0,
"start_time": "10:40:55.582273",
"duration": 19.374,
"__id__": "test_run"
}
}
}
json parsed down with jq to just the stdout
$ sudo salt salt00\* state.apply tests.test3 --out=json | jq '.|.[]|."cmd_|-test_run_|-echo test_|-run"|.changes.stdout'
"test"
Also, for the record it is considered bad practice to put code that changes the system into jinja. Jinja always runs when a template is rendered and there is no way to control if it happens so just running test=true tests will still run the jinja code that makes changes which could be very harmful to your systems.

how to read tab separated line into variables

I wrote this function in zsh
function test() {
test="a\tb\tc"
while IFS=$'\t' read -r user host key; do
echo "user: $user"
echo "host: $host"
echo "key: $key"
done <<< "$test"
}
The output was:
user: a b c
host:
key:
if instead of
... IFS=$'\t' read -r ...
I change it to
... IFS='\t' read -r ...
the output is
user: a
host:
key: b c
Just what is going on?
I would like to read the tab separated line and set my variables accordingly.
Changing the double-quotes to $'...' (single quotes preceded by a $) could rescue for the variable $test:
test=$'a\tb\tc'
Here is the zsh manual for QUOTING (double-quoting and $'...'):
QUOTING
...
A string enclosed between $' and ' is processed the same way as the string arguments of the print builtin
...
Inside double quotes (""), parameter and command substitution occur, and \ quotes the characters \, `, ", $, and the first character of $histchars (default !).
--- zshmisc(1), QUOTING
For example:
"\$" -> $, "\!" -> ! etc.
"\t" -> \t (zsh does not recognize as tab this case), "\a" -> \a etc.
It does not treat the escape sequence \t as tab when it is used inside double quotes, so "a\tb\tc" does not mean "atabbtabc". (But things are a little more complicated: builtin echo recognizes the escape sequence \t.)
(1) ... IFS=$'\t' read -r ... (the original form)
Because expanding "$test" dose not have any tab characters, so read assigns the whole line to $user:
user: a b c
host:
key:
(But echo recognizes the escape sequence \t as the tab.)
(2) ... IFS='\t' read -r ...
Again, expanding "$test" does not have any tab characters, so read separate the field by \ and t according $IFS.
a\t\b\tc splits into a (to $user), \(separator), `` (empty to $host), t(separator), and the rest of the line (b\tc to $key):
user: a
host:
key: b c
(But again, echo recognizes the escape sequence \t as the tab.)
Here is the code changed from test="..." to test=$'...':
function test() {
test=$'a\tb\tc'
while IFS=$'\t' read -r user host key; do
echo "user: $user"
echo "host: $host"
echo "key: $key"
done <<< "$test"
}
test
The output is:
user: a
host: b
key: c
PS: it is worth reading POSIX's Quoting specification, which is simpler than zsh's (https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02)

Sed replacing pattern starts with but not contains string

I have the following text file: file.text
Cygwin
value: c
Unix-keep
value: u
Linux
value: l
Unix-16
value: u
Solaris
value: s
Unix-replace-1
value: u
Unix-replace-2
value: u
I want to replace all lines values based on previous string : starting with Unix- but excluding containing string keep
So in the end affected lines sould be the ones after:
Unix-replace-1 and Unix-replace-2 with value value: NEW_VERSION
Expected output:
Cygwin
value: c
Unix-keep
value: u
Linux
value: l
Unix-16
value: NEW_VERSION
Solaris
value: s
Unix-replace-1
value: NEW_VERSION
Unix-replace-2
value: NEW_VERSION
I tried following sed script:
sed '/^Unix-/ {n;s/.*/value: NEW_VERSION/}' file.text
but this can only get starting but not excluding -keep substrings. I am not sure how to combine the excluded ones.
Any ideas?
$ awk -v new='NEW_VERSION' 'f{$0=$1 FS new} {f=(/^Unix-/ && !/keep/)} 1' file
Cygwin
value: c
Unix-keep
value: u
Linux
value: l
Unix-16
value: NEW_VERSION
Solaris
value: s
Unix-replace-1
value: NEW_VERSION
Unix-replace-2
value: NEW_VERSION
Using a proper xml parser (the question was originally plain XML before edited by OP):
The XML edited to be valid (closing tags missing) :
<project>
<Cygwin>
<version>c</version>
</Cygwin>
<Unix-keep>
<version>u</version>
</Unix-keep>
<Linux>
<version>l</version>
</Linux>
<Solaris>
<version>s</version>
</Solaris>
<Unix-replace-1>
<version>u</version>
</Unix-replace-1>
<AIX>
<version>a</version>
</AIX>
<Unix-replace-2>
<version>u</version>
</Unix-replace-2>
</project>
The command:
xmlstarlet ed -u '//project/*
[starts-with(name(), "Unix") and not(starts-with(name(), "Unix-keep"))]
/version
' -v 'NEW_VERSION' file
To edit the file in place, use
xmlstarlet ed -L -u ...

Ansible loop on var prompt

I would like to create several node on my bigip. For that I want to do a loop on my var prompt and register each value in my variable {{node_list}}.
This is what I've tried
- name: node creation
hosts: F5
gather_facts: no
connection: local
vars_prompt:
## ASK NUMBER OF NODES
- name: node_nb
prompt: "number of nodes"
private: no
## ASK THE NAME AND IP WITH FORMAT NAME;IP
- name: node_list
prompt: "name and Ip of the node like that toto;1.1.1.1"
private: no
with_sequence: count={{ node_nb | int }}
- name: Create node
bigip_node:
user: '{{ ansible_user }}'
password: '{{ ansible_password }}'
server: 'xxxxx'
host: '{{ (item).split(";")[1] }}'
name: '{{ (item).split(";")[0] }}'
partition: 'Common'
state: present
validate_certs: false
with_items: '{{ node_list }}'
First :
My var prompt don't loop if for example I specify "4" in {{ node_nb }}. The question is prompt one time but I want 4 times.
Second:
I would register all informations of the value in input each time in a list. If I want 4 nodes I need to have 4 items in my list
Just have them enter the list separated by spaces, since you are already using ; to separate node names from IPs, and it additionally saves you the trouble of having to prompt for the count because the count will be however many items there are in the list
with_sequence only works with tasks.
so just keep one variable node_list in to the vars_prompt and pass ',' separated list ['asd;1.1.1.1','sdf;2.2.2.2'] as a value.

How to extract text from a file which appears one or more times in each line?

I have a text file which have 1 or more email ids in each line. E.g.
id:123, name:test, id: 5678, name john, address:new york
id:567, name:bob
id:3643, name:meg, id: 6721, name kate, address:la
Now, the problem is id:value may appear one or more times in a single line. How do I extract all id:value pairs so that the output is,
id:123, id:5678
id:567
id:3643, id:6721
I tried egrep -o but that is putting each id:value pair in a separate line.
sed/awk should do the trick but I am a noob
Do not want to use Perl as that will require a Perl installation.
EDIT:
On further analysis of the data files, I am seeing inconsistent separators, i.e. not all lines are , separated. Some are even separated with : and |. Also, , is appearing within the address value field. i.e. address:52nd st, new york. Can this be done in awk using a regex expression?
If your content is in the file test.txt then the following command:
cat test.txt | sed 's/ *: */:/g' | grep -o 'id:[0-9]*'
will return:
id:123
id:5678
id:567
id:3643
id:6721
The sed command is to remove any spaces adjacent to the colon, yielding an output of:
id:123, name:test, id:5678, name john, address:new york
id:567, name:bob
id:3643, name:meg, id:6721, name kate, address:la
and the grep -o command finds all matches to id: proceeded by zero or more numbers, with the -o to return only the matching part of the input string.
Per the man page:
-o, --only-matching Print only the matched (non-empty) parts of a matching
line, with each such part on a separate output line.
(FYI, the grep and sed commands are using regular expressions.)
EDIT:
Sorry, I didn't read carefully. I see that you object to the -o output format of one value per line. Back to the drawing board...
Note: If the reason you are opposed to the -o output is to preserve line numbers, using grep -no will give the following output (where the first number is the line number):
1:id:123
1:id:5678
2:id:567
3:id:3643
3:id:6721
Maybe that helps?
This might work for you (GNU sed):
sed -r 's/\<id:\s*/\n/g;s/,[^\n]*//g;s/\n/, id:/g;s/^, //' file
Convert the words id: and any following spaces to a unique token (in this case \n). Delete anyting following a , upto a \n. Replace the \n by the token , id: and then delete the leading ,.
This should work:
awk -F, '{id=0;for(i=1;i<=NF;i++) if($i~/id:/) id=id?id FS $i:$i; print id}' file
Test:
$ cat file
id:123, name:test, id: 5678, name john, address:new york
id:567, name:bob
id:3643, name:meg, id: 6721, name kate, address:la
$ awk -F, '{id=0;for(i=1;i<=NF;i++) if($i~/id:/) id=id?id FS $i:$i; print id}' file
id:123, id: 5678
id:567
id:3643, id: 6721
perl -lne 'push #a,/id:[^,]*/g;print "#a";undef #a' your_file
Tested Below:
> cat temp
id:123, name:test, id: 5678, name john, address:new york
id:567, name:bob
id:3643, name:meg, id: 6721, name kate, address:la
> perl -lne 'push #a,/id:[^,]*/g;print "#a";undef #a' temp
id:123 id: 5678
id:567
id:3643 id: 6721
>
This is just a variation of an answer allready given..I personaly prefere the script verion in a file more than the command line (better control, readability)
id.txt
id:1, name:test, id:2, name john, address:new york
id:3, name:bob
id:4, name:meg, id:5, name kate, address:la
id.akw
{
i=0
for(i=1;i<=NF;i++)
{ if($i~/id:/)
id=id?id $i:$i;}
print id
id=""
}
call: awk -f id.awk id.txt
output:
id:1, id:2,
id:3,
id:4, id:5,

Resources