Background
The tr command translates, delete, or squeezes characters from standard input. I am trying to create a cshell alias that outputs the setenv command such that each file or directory in the path is displayed on a new line.
In other words, I want this example output of setenv:
PATH=/some/dir:/some/second/dir:/some/third/dir
PROFILES= file:///some/file/here.txt;file:///some/second/file.xml;file:///some/third/file/there.pl
To become this:
PATH= /some/dir
/some/second/dir
/some/third/dir
PROFILES= file:///some/file/here.txt
file:///some/second/file.xml
file:///some/third/file/there.pl
Related Aliases that work
I have created aliases that output the individual environment variables like above:
For PATH:
alias readablePath = 'echo "$PATH" | tr : '\'\\\n\'' '
For PROFILES:
alias readableProfiles= 'echo "$PROFILES" | tr \; 'echo "$PATH" | tr : '\'\\\n\'' '
Attempted aliases that do not work
These are the aliases that I have tried that return tr: no match.
alias readEnv 'echo "setenv" | tr [:\;] '\'\\\n\'' '
alias readEnv 'echo setenv | tr [:\;] '\'\\\n\'' '
alias readEnv 'echo setenv | tr [:\\;] '\'\\\n\'' '
alias readEnv 'echo setenv | tr [:;] '\'\\\n\'' '
These are the aliases that I have tried that return the string "setenv".
alias readEnv 'echo setenv | tr "[:;]" '\'\\\n\'' '
alias readEnv 'echo "setenv" | tr "[:;]" '\'\\\n\'' '
Question
How do I create an alias that successfully takes the output of setenv and inserts a newline where there is a ":" or a ";"?
To replace multiple characters in your (unix/linux) command line alias, you should rather use sed and regular expressions, as in Search and replace with sed when dots and underscores are present .
So something like:
sed 's/\.\|,/\\n/g'
where \. finds a dot (needs to be escaped, else it finds any character), \| is the or operation, also escaped, and the new line also must have its \\ escaped.
Related
I would like my grammar to be able to match either a single line string assignment terminated by a newline (\r\n or \n), possibly with a comment at the end, or a multiline assignment, denoted by double quotes. So for example:
key = value
key = spaces are allowed
key = until a new line or a comment # this is a comment
key = "you can use quotes as well" # this is a comment
key = "and
with quotes
you can also do
multiline"
Is that doable? I've been bashing my head on this, and got everything working except the multiline. It seems so simple, but the rules simply won't match appropriately.
Add on: this is just a part of a bigger grammar.
Looking at your example input:
# This is the most simple configuration
title = "FML rulez"
# We use ISO notations only, so no local styles
releaseDateTime = 2020-09-12T06:34
# Multiline strings
description = "So,
I'm curious
where this will end."
# Shorcut string; no quotes are needed in a simple property style assignment
# Or if a string is just one word. These strings are trimmed.
protocol = http
# Conditions allow for overriding, best match wins (most conditions)
# If multiple condition sets equally match, the first one will win.
title[env=production] = "One config file to rule them all"
title[env=production & os=osx] = "Even on Mac"
# Lists
hosts = [alpha, beta]
# Hierarchy is implemented using groups denoted by curly brackets
database {
# indenting is allowed and encouraged, but has no semantic meaning
url = jdbc://...
user = "admin"
# Strings support default encryption with a external key file, like maven
password = "FGFGGHDRG#$BRTHT%G%GFGHFH%twercgfg"
# groups can nest
dialect {
database = postgres
}
}
servers {
# This is a table:
# - the first row is a header, containing the id's
# - the remaining rows are values
| name | datacenter | maxSessions | settings |
| alpha | A | 12 | |
| beta | XYZ | 24 | |
| "sys 2" | B | 6 | |
# you can have sub blocks, which are id-less groups (id is the column)
| gamma | C | 12 | {breaker:true, timeout: 15} |
# or you reference to another block
| tango | D | 24 | $environment |
}
# environments can be easily done using conditions
environment[env=development] {
datasource = tst
}
environment[env=production] {
datesource = prd
}
I'd go for something like this:
grammar TECL;
input_file
: configs EOF
;
configs
: NL* ( config ( NL+ config )* NL* )?
;
config
: property
| group
| table
;
property
: WORD conditions? ASSIGN value
;
group
: WORD conditions? NL* OBRACE configs CBRACE
;
conditions
: OBRACK property ( AMP property )* CBRACK
;
table
: row ( NL+ row )*
;
row
: PIPE ( col_value PIPE )+
;
col_value
: ~( PIPE | NL )*
;
value
: WORD
| VARIABLE
| string
| list
;
string
: STRING
| WORD+
;
list
: OBRACK ( value ( COMMA value )* )? CBRACK
;
ASSIGN : '=';
OBRACK : '[';
CBRACK : ']';
OBRACE : '{';
CBRACE : '}';
COMMA : ',';
PIPE : '|';
AMP : '&';
VARIABLE
: '$' WORD
;
NL
: [\r\n]+
;
STRING
: '"' ( ~[\\"] | '\\' . )* '"'
;
WORD
: ~[ \t\r\n[\]{}=,|&]+
;
COMMENT
: '#' ~[\r\n]* -> skip
;
SPACES
: [ \t]+ -> skip
;
which will parse the example in the following parse tree:
And the input:
key = value
key = spaces are allowed
key = until a new line or a comment # this is a comment
key = "you can use quotes as well" # this is a comment
key = "and
with quotes
you can also do
multiline"
into the following:
For now: multiline quoted works, spaces in unquoted string not.
As you can see in the tree above, it does work. I suspect you used part of the grammar in your existing one and that doesn't work.
[...] and am I the process inserting the actions.
I would not embed actions (target code) inside your grammar: it makes it hard to read, and making changes to the grammar will be harder to do. And of course, your grammar will only work for 1 language. Better use a listener or visitor instead of these actions.
Good luck!
I'd like to perform a series of sed commands on lines of a file roster.txt only beginning with a keyword. For example:
Employee : Kiara 20 hours#8.25
Employee : Connor 25 hours#8.00
Employee : Dylan 30 hours#9.00
Becomes:
Employee : Kiara_20_hoursat8dot25
Employee : Connor_25_hoursat8dot00
Employee : Dylan_30_hoursat9dot00
I know the sed commands to make the changes I just wanted a way to peform them on lines starting with "employee". Maybe
awk '$1 == "Employee" {sed -i -e 's/\./dot/g' roster.txt}' roster.txt
$ cat roster.txt
foo : bar#baz.123
Employee : Kiara 20 hours#8.25
Employee : Connor 25 hours#8.00
Employee : Dylan 30 hours#9.00
$ awk 'BEGIN{FS=OFS=" : "} $1=="Employee"{gsub(/ /,"_",$2); gsub(/#/,"at",$2); gsub(/\./,"dot",$2)} 1' roster.txt
foo : bar#baz.123
Employee : Kiara_20_hoursat8dot25
Employee : Connor_25_hoursat8dot00
Employee : Dylan_30_hoursat9dot00
awk supports substitution commands as well - sub to replace first occurrence and gsub to replace all occurrences. Also allows to change only specific field
BEGIN{FS=OFS=" : "} use : as input/output field separator
gsub(/ /,"_",$2) replace all spaces with _ only for second field
Similarly other substitutions as required
1 at end of command is idiomatic way to print the line, includes any changes made
See also awk save modifications in place
I'd write:
sed '/^Employee :/ {s/#/at/; s/\./dot/; s/ /_/3g}' <<END
Employee : Kiara 20 hours#8.25
Employee : Connor 25 hours#8.00
Employee : Dylan 30 hours#9.00
Foo : bar baz#qux.blah
END
Employee : Kiara_20_hoursat8dot25
Employee : Connor_25_hoursat8dot00
Employee : Dylan_30_hoursat9dot00
Foo : bar baz#qux.blah
Requires GNU sed for the 3g modifier of the s command
You really do not need to use both tools, either will do everything you need.
sed solution:
sed -i -e 's/^Employee : \([^ ]*\) \([0-9]*\) hours#\([0-9]\)\.\([0-9]*\)/Employee : \1_\2_hoursat\3dot\4/' roster.txt
Edit after comment:
If you want a very generic replacement using only sed that works on your sample:
sed -i -e 's/\([^:]\) \([^:]\)/\1_\2/g' -e 's/#/at/g' -e 's/\./dot/g' roster.txt
So I have a simple string that I was hoping to run through a ragel state machine.
key1=value1; key2="value2"; key3=value3
Here is a simplified version of my ragel
# Key Value Parts
name = ( token+ ) %on_name ;
value = ( ascii+ -- (" " | ";" | "," | "\r" | "\n" | "\\" | "\"" ) ) %on_value ;
pair = ( name "=" (value | "\"" value "\"") "; " ) ; ## ISSUE WITH FORCING ;
string = ( pair )+ ;
# MACHINE
main := string >begin_parser #end_parser ;
The issue I'm having is that I'll never have a semicolon after the last key/value pair, so I'd like it to be optional, but when I do that the state machine finds several patches for the value. Is there some sort of syntax where I can say pair has to end with a (";" | *eof*)?
I did change, my main line to this, but it seems like a hack and doesn't really work with some of the other things I'd like to do with this state machine.
main := string >begin_parser #end_parser $/on_value;
I was too wrapped up in Ragel syntax and wasn't think about how I was processing. Instead of trying to add an optional semi-colon onto the end I should have force one on the front after it had already process a key value pair.
pair = ( name "=" (value | "\"" value "\"") ) ;
string = pair ( "; " pair )* ;
use any unix/linux commands(awk) ,input is like :
78/WESDA.99/C/694883/O///BFPC0T/C/BFPC0T X/C
out put should be:
78,WESDA.99/C,694883,O,,,BFPC0T,C,BFPC0T X,C
you can use one of these;
sed 's|/|,|g' yourFile
tr '/' ',' yourFile
awk '{gsub(/\//,",",$0);print $0}' yourFile
Test;
$ echo "78/WESDA.99/C/694883/O///BFPC0T/C/BFPC0T X/C " | sed 's|/|,|g'
78,WESDA.99,C,694883,O,,,BFPC0T,C,BFPC0T X,C
$ echo "78/WESDA.99/C/694883/O///BFPC0T/C/BFPC0T X/C " | tr '/' ','
78,WESDA.99,C,694883,O,,,BFPC0T,C,BFPC0T X,C
The contents of myfile.txt file are as follows:
| dbname |
| dbname1 |
| dbname2 |
The following command is expected to generate the output as shown below:
cat myfile.txt | awk '{print "mysql -uroot -Bse \"call mysql.p_check_fk_constraint_violations('\'$2\'','\''%'\'')\""}'
Expected Output:
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('dbname','%')"
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('dbname1','%')"
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('dbname2','%')"
But the actual output is:
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('','%')"
How do I add the database names in awk statement?
This should do it -
[jaypal~/Temp]$ cat db.file
| dbname |
| dbname1 |
| dbname2 |
Here we are substituting the second field with your text and using "&" matches the field getting substituted.
[jaypal~/Temp]$ awk -F\| '{sub($2,"mysql \-uroot \-Bse \"call mysql.p_check_fk_constraint_violations\(&,\%\)\""); print $2}' db.file
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations( dbname ,%)"
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations( dbname1 ,%)"
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations( dbname2 ,%)"
Alternatively as Teudimundo suggested, you can do -
[jaypal~/Temp]$ cat db.file | awk '{print "mysql -uroot -Bse \"call mysql.p_check_fk_constraint_violations("$2",'%')\""}'
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations(dbname,%)"
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations(dbname1,%)"
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations(dbname2,%)"
UPDATE
[jaypal~/Temp]$ cat db.file | awk '{print "mysql -uroot -Bse \"call mysql.p_check_fk_constraint_violations('"'"'"$2"'"'"', '"'"'%'"'"')"}'
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('dbname', '%')
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('dbname1', '%')
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('dbname2', '%')
[jaypal~/Temp]$ awk '{ print $2 }' db.file | awk '{sub($1,"mysql \-uroot \-Bse \"call mysql.p_check_fk_constraint_violations\('"'"'&'"'"','"'"'%'"'"'\)\""); print $0}'
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('dbname','%')"
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('dbname1','%')"
mysql -uroot -Bse "call mysql.p_check_fk_constraint_violations('dbname2','%')"
here : '" $2 "' you are closing the first awk ' char, and so " $2 " is interpreted by the shell.
You're running into problems because of the single quotes, as Teudimundo said. To fix it, you need to replace each single quote ' that you'd like to embed with this '"'"', giving this awk command:
awk '{print "mysql -uroot -Bse \"call mysql.p_check_fk_constraint_violations('"'"'$2'"'"', '"'"'%'"'"')"}'
This works because the '"'"' first ends the single-quoted string for the awk command, begins a new double-quoted string containing a single quote, then starts a new single-quoted string with the rest of the awk command. Since adjacent strings are concatenated in the shell, this strange-seeming approach produces the string you need.
It suppose that it will be far better to use mysql procedure, instead of jumping in and out of mysql using shell scripting ...
I'm not familiar with mysql's procedural language, but I'm sure that if you search Internet
you can quickly come up with a simple procedure, something like this:
delimiter //
drop procedure run_proc //
create procedure run_proc()
begin
declare done boolean default 0;
declare l_db_name varchar(100);
declare cur_db_names cursor
for
select
schema_name
from
information_schema.schemata;
declare continue handler for
sqlstate '02000' set done=1;
open cur_db_names;
repeat
fetch cur_db_names into l_db_name;
call mysql.p_check_fk_constraint_violations(l_db_name,'%');
until done end repeat;
close cur_db_names;
end;
//
delimiter ;
call run_proc;