How would I use strings inside a for loop? - linux
I apologize for my question not being specific enough but I have no choice. So I received an assignment that hasn't been completely covered in the learning material (even the person assigned to help students is having trouble helping me) since this is beyond basic bash scripting. I'm not expecting anybody to do my assignment but if I can get a clue or an idea it'll be very helpful!
My assignment:
Code a script in bash linux that will use user's input of number of rows and number of columns, and print 'hello' strong according to the user's input, like so:
For example:
User's input of number of columns:2
User's input of number of rows: 3
hello hello
hello hello
hello hello
I thought in this direction but I can't figure it out and will appreciate any help :)
echo -e 'Please enter number of rows: \n'
read rows
echo -e 'Please enter number of columns: \n'
read columns
string='hello'
for i in $columns
do
echo $string
string+=$string
done
(this is as far as I got with the first loop as what ive done here doesn't work)
Check this out:
#!/bin/bash
read -p 'Please enter number of rows and columns: ' rows columns # prompt and read both vars at once
string='hello' # set string
printf -v row "%${columns}s" # create var $row consists on N(columns) spaces
row=${row//' '/"$string "} # recreate var $row changing spaces to "$string "
printf -v col "%${rows}s" # create var $col consists on N(rows) spaces
all=${col//' '/"$row\n"} # create full set in var $all by changing spaces to "$row\n"
printf "$all" # print all
Testing:
$ ./ex
Please enter number of rows and columns: 3 5
hello hello hello hello hello
hello hello hello hello hello
hello hello hello hello hello
With two loops:
#!/bin/bash
string='hello'
read -p "x:" x
read -p "y:" y
for ((j=0; j<$y; j++)); do
for ((i=0; i<$x; i++)); do
echo -n "$space$string"
space=" "
done
space=""
echo
done
See: man bash
To read inputs you can use read builtin. For example
read -r row column
Then you can use $row and $column variables.
You'd need a nested for loop to print row x column times.
To not print newlines, use -n option of echo.
Refer help read, help for, and help echo for details. You can obviously Google these terms, too ;-)
Do yo want to golf it? :)
printf "%$((rows*columns))s" | fold -w "$columns" | sed 's/ /hello /g'
To prompt the user for rows and colums, use the read builtin:
read -p 'Enter rows: ' rows
read -p 'Enter columns: ' columns
I prefer to get my arguments on the command line.
Accordingly, one implementation (with no error checking...):
rows=$1 # first arg is rows to output
cols=$2 # second column is columns wanted
str=$3 # third arg is the string to print
while (( rows-- )) # post-decrement rows
do c=$cols # reset a column count for each new row
while (( c-- )) # post-decrement columns done
do printf "%s " "$str" # print the string with a trailing space, NO newline
done
printf "\n" # print a newline at the end of each row
done
Make sure you understand ((...)) arithmetic processing, printf, and command line argument parsing. All these are available in the documentation.
For extra credit, do proper error checking of your inputs.
If you need to read the inputs from stdin instead of the command line, replace
rows=$1 # first arg is rows to output
cols=$2 # second column is columns wanted
str=$3 # third arg is the string to print
with
read rows cols str
Better, read each with an appropriate prompt - again, details available in the manual.
Good luck.
Related
bash shell script transmission variable value space processing, how to space data it
bash shell script transmission variable value space processing, how to space data it **#FileList. list.txt Delivery list belowt** $1 $2 $3 xxxx1.com_1 Hello_2 Hello_3 - Hello xxxx2.com_1 Hello_2 Hello_3 - Hello get_list() { now_list=${1} #Pass list while read -r line; do #Circular reading now_list_url=$(echo "${line}"|awk -F ' ' '{print $1}') #url variable1 now_list_keyword=$(echo "${line}"|awk -F ' ' '{print $2}') #keyword variable2 now_list_title=$(echo "${line}"|awk -F ' ' '{print $3}') #title variable3 #print echo "url:${now_list_url}" result:xxxx1.com_1 echo "keyword:${now_list_keyword}" result:Hello_2 echo "title:${now_list_title}" result:Hello_3 #Due to the empty grid of the transmission variable 3 result:Hello_3 - Hello #And want to be correct done < "${now_list}" } #Run get_list list.txt` Transfer variable 3 due to space errors:Hello_3 I want correct transmission variables 3 due to space and finally the correct result:Hello_3 - Hello #And here is because the transmission variable is incomplete,The result I finally need is the value of the value of the output complete variable 3 in the value of the complete variable3 = "Hello_3 - hello" Because of work needs, a lot of list processing, I will save the variable value in the text How should I deal with it thanks
If I understand what you're trying to do, you should let read split the fields, instead of using awk: while read -r now_list_url now_list_keyword now_list_title; do echo "url:${now_list_url}" # result: xxxx1.com_1 echo "keyword:${now_list_keyword}" # result: Hello_2 echo "title:${now_list_title}" # result: Hello_3 - Hello done < "${now_list}" Since three variables were given to read, it'll try to split the line into three fields. When there are more than three space-separated things in the line, all the extra gets put in the last variable. See BashFAQ #1: "How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?" for more information.
Extract orders and match to trades from two files
I have two attached files (orders1.txt and trades1.txt) I need to write a Bash script (possibly awk?) to extract orders and match them to trades. The output should produce a report that prints comma separated values containing “ClientID, OrderID, Price, Volume”. In addition to this for each client, I need to print the total volume and turnover (turnover is the subtotal of price * volume on each trade). Can someone please help me with a bash script that will do the above using the attached files? Any help would be greatly appreciated orders1.txt Entry Time, Client ID, Security ID, Order ID 25455410,DOLR,XGXUa,DOLR1435804437 25455410,XFKD,BUP3d,XFKD4746464646 25455413,QOXA,AIDl,QOXA7176202067 25455415,QOXA,IRUXb,QOXA6580494597 25455417,YXKH,OBWQs,YXKH4575139017 25455420,JBDX,BKNs,JBDX6760353333 25455428,DOLR,AOAb,DOLR9093170513 25455429,JBDX,QMP1Sh,JBDX2756804453 25455431,QOXA,QIP1Sh,QOXA6563975285 25455434,QOXA,XMUp,QOXA5569701531 25455437,XFKD,QLOJc,XFKD8793976660 25455438,YXKH,MRPp,YXKH2329856527 25455442,JBDX,YBPu,JBDX0100506066 25455450,QOXA,BUPYd,QOXA5832015401 25455451,QOXA,SIOQz,QOXA3909507967 25455451,DOLR,KID1Sh,DOLR2262067037 25455454,DOLR,JJHi,DOLR9923665017 25455461,YXKH,KBAPBa,YXKH2637373848 25455466,DOLR,EPYp,DOLR8639062962 25455468,DOLR,UQXKz,DOLR4349482234 25455474,JBDX,EFNs,JBDX7268036859 25455481,QOXA,XCB1Sh,QOXA4105943392 25455486,YXKH,XBAFp,YXKH0242733672 25455493,JBDX,BIF1Sh,JBDX2840241688 25455500,DOLR,QSOYp,DOLR6265839896 25455503,YXKH,IIYz,YXKH8505951163 25455504,YXKH,ZOIXp,YXKH2185348861 25455513,YXKH,MBOOp,YXKH4095442568 25455515,JBDX,P35p,JBDX9945514579 25455524,QOXA,YXOKz,QOXA1900595629 25455528,JBDX,XEQl,JBDX0126452783 25455528,XFKD,FJJMp,XFKD4392227425 25455535,QOXA,EZIp,QOXA4277118682 25455543,QOXA,YBPFa,QOXA6510879584 25455551,JBDX,EAMp,JBDX8924251479 25455552,QOXA,JXIQp,QOXA4360008399 25455554,DOLR,LISXPh,DOLR1853653280 25455557,XFKD,LOX14p,XFKD1759342196 25455558,JBDX,YXYb,JBDX8177118129 25455567,YXKH,MZQKl,YXKH6485420018 25455569,JBDX,ZPIMz,JBDX2010952336 25455573,JBDX,COPe,JBDX1612537068 25455582,JBDX,HFKAp,JBDX2409813753 25455589,QOXA,XFKm,QOXA9692126523 25455593,XFKD,OFYp,XFKD8556940415 25455601,XFKD,FKQLb,XFKD4861992028 25455606,JBDX,RIASp,JBDX0262502677 25455608,DOLR,HRKKz,DOLR1739013513 25455615,DOLR,ZZXp,DOLR6727725911 25455623,JBDX,CKQPp,JBDX2587184235 25455630,YXKH,ZLQQp,YXKH6492126889 25455632,QOXA,ORPz,QOXA3594333316 25455640,XFKD,HPIXSh,XFKD6780729432 25455648,QOXA,ABOJe,QOXA6661411952 25455654,XFKD,YLIp,XFKD6374702721 25455654,DOLR,BCFp,DOLR8012564477 25455658,JBDX,ZMDKz,JBDX6885176695 25455665,JBDX,CBOe,JBDX8942732453 25455670,JBDX,FRHMl,JBDX5424320405 25455679,DOLR,YFJm,DOLR8212353717 25455680,XFKD,XAFp,XFKD4132890550 25455681,YXKH,PBIBOp,YXKH6106504736 25455684,DOLR,IFDu,DOLR8034515043 25455687,JBDX,JACe,JBDX8243949318 25455688,JBDX,ZFZKz,JBDX0866225752 25455693,QOXA,XOBm,QOXA5011416607 25455694,QOXA,IDQe,QOXA7608439570 25455698,JBDX,YBIDb,JBDX8727773702 25455705,YXKH,MXOp,YXKH7747780955 25455710,YXKH,PBZRYs,YXKH7353828884 25455719,QOXA,QFDb,QOXA2477859437 25455720,XFKD,PZARp,XFKD4995735686 25455722,JBDX,ZLKKb,JBDX3564523161 25455730,XFKD,QFH1Sh,XFKD6181225566 25455733,JBDX,KWVJYc,JBDX7013108210 25455733,YXKH,ZQI1Sh,YXKH7095815077 25455739,YXKH,XIJp,YXKH0497248757 25455739,YXKH,ZXJp,YXKH5848658513 25455747,JBDX,XASd,JBDX4986246117 25455751,XFKD,XQIKz,XFKD5919379575 25455760,JBDX,IBXPb,JBDX8168710376 25455763,XFKD,EVAOi,XFKD8175209012 25455765,XFKD,JXKp,XFKD2750952933 25455773,XFKD,PTBAXs,XFKD8139382011 25455778,QOXA,XJp,QOXA8227838196 25455783,QOXA,CYBIp,QOXA2072297264 25455792,JBDX,PZI1Sh,JBDX7022115629 25455792,XFKD,XIKQl,XFKD6434550362 25455792,DOLR,YKPm,DOLR6394606248 25455796,QOXA,JXOXPh,QOXA9672544909 25455797,YXKH,YIWm,YXKH5946342983 25455803,YXKH,JZEm,YXKH5317189370 25455810,QOXA,OBMFz,QOXA0985316706 25455810,QOXA,DAJPp,QOXA6105975858 25455810,JBDX,FBBJl,JBDX1316207043 25455819,XFKD,YXKm,XFKD6946276671 25455821,YXKH,UIAUs,YXKH6010226371 25455828,DOLR,PTJXs,DOLR1387517499 25455836,DOLR,DCEi,DOLR3854078054 25455845,YXKH,NYQe,YXKH3727923537 25455853,XFKD,TAEc,XFKD5377097556 25455858,XFKD,LMBOXo,XFKD4452678489 25455858,XFKD,AIQXp,XFKD5727938304 trades1.txt # The first 8 characters is execution time in microseconds since midnight # The next 14 characters is the order ID # The next 8 characters is the zero padded price # The next 8 characters is the zero padded volume 25455416QOXA6580494597 0000013800001856 25455428JBDX6760353333 0000007000002458 25455434DOLR9093170513 0000000400003832 25455435QOXA6563975285 0000034700009428 25455449QOXA5569701531 0000007500009023 25455447YXKH2329856527 0000038300009947 25455451QOXA5832015401 0000039900006432 25455454QOXA3909507967 0000026900001847 25455456DOLR2262067037 0000034700002732 25455471YXKH2637373848 0000010900006105 25455480DOLR8639062962 0000027500001975 25455488JBDX7268036859 0000005200004986 25455505JBDX2840241688 0000037900002029 25455521YXKH4095442568 0000046400002150 25455515JBDX9945514579 0000040800005904 25455535QOXA1900595629 0000015200006866 25455533JBDX0126452783 0000001700006615 25455542XFKD4392227425 0000035500009948 25455570XFKD1759342196 0000025700007816 25455574JBDX8177118129 0000022400000427 25455567YXKH6485420018 0000039000008327 25455573JBDX1612537068 0000013700001422 25455584JBDX2409813753 0000016600003588 25455603XFKD4861992028 0000017600004552 25455611JBDX0262502677 0000007900003235 25455625JBDX2587184235 0000024300006723 25455658XFKD6374702721 0000046400009451 25455673JBDX6885176695 0000010900009258 25455671JBDX5424320405 0000005400003618 25455679DOLR8212353717 0000041100003633 25455697QOXA5011416607 0000018800007376 25455696QOXA7608439570 0000013000007463 25455716YXKH7747780955 0000037000006357 25455719QOXA2477859437 0000039300009840 25455723XFKD4995735686 0000045500009858 25455727JBDX3564523161 0000021300000639 25455742YXKH7095815077 0000023000003945 25455739YXKH5848658513 0000042700002084 25455766XFKD5919379575 0000022200003603 25455777XFKD8175209012 0000033300006350 25455788XFKD8139382011 0000034500007461 25455793QOXA8227838196 0000011600007081 25455784QOXA2072297264 0000017000004429 25455800XFKD6434550362 0000030000002409 25455801QOXA9672544909 0000039600001033 25455815QOXA6105975858 0000034800008373 25455814JBDX1316207043 0000026500005237 25455831YXKH6010226371 0000011400004945 25455838DOLR1387517499 0000046200006129 25455847YXKH3727923537 0000037400008061 25455873XFKD5727938304 0000048700007298 I have the following script: ''' #!/bin/bash declare -A volumes declare -A turnovers declare -A orders # Read the first file, remembering for each order the client id while read -r line do # Jump over comments if [[ ${line:0:1} == "#" ]] ; then continue; fi; details=($(echo $line | tr ',' " ")) order_id=${details[3]} client_id=${details[1]} orders[$order_id]=$client_id done < $1 echo "ClientID,OrderID,Price,Volume" while read -r line do # Jump over comments if [[ ${line:0:1} == "#" ]] ; then continue; fi; order_id=$(echo ${line:8:20} | tr -d '[:space:]') client_id=${orders[$order_id]} price=${line:28:8} volume=${line: -8} echo "$client_id,$order_id,$price,$volume" price=$(echo $price | awk '{printf "%d", $0}') volume=$(echo $volume | awk '{printf "%d", $0}') order_turnover=$(($price*$volume)) old_turnover=${turnovers[$client_id]} [[ -z "$old_turnover" ]] && old_turnover=0 total_turnover=$(($old_turnover+$order_turnover)) turnovers[$client_id]=$total_turnover old_volumes=${volumes[$client_id]} [[ -z "$old_volumes" ]] && old_volumes=0 total_volume=$((old_volumes+volume)) volumes[$client_id]=$total_volume done < $2 echo "ClientID,Volume,Turnover" for client_id in ${!volumes[#]} do volume=${volumes[$client_id]} turnover=${turnovers[$client_id]} echo "$client_id,$volume,$turnover" done Can anyone think of anything more elegant? Thanks in advance C
Assumption 1: the two files are ordered, so line x represents an action that is older than x+1. If not, then further work is needed. The assumption makes our work easier. Let's first change the delimiter of traders into a comma: sed -i 's/ /,/g' traders.txt This will be done in place for sake of simplicity. So, you now have traders which is comma separated, as is orders. This is the Assumption 2. Keep working on traders: split all columns and add titles1. More on the reasons why in a moment. gawk -i inplace -v INPLACE_SUFFIX=.bak 'BEGINFILE{FS=",";OFS=",";print "execution time,order ID,price,volume";}{print substr($1,1,8),substr($1,9),substr($2,1,9),substr($2,9)}' traders.txt Ugly but works. Now let's process your data using the following awk script: BEGIN { FS="," OFS="," } { if (1 == NR) { getline line < TRADERS # consume title line print "Client ID,Order ID,Price,Volume,Turnover"; # consume title line. Remove print to forget it getline line < TRADERS # reads first data line split(line, transaction, ",") next } if (transaction[2] == $4) { print $2, $4, transaction[3], transaction[4], transaction[3]*transaction[4] getline line < TRADERS # reads new data line split(line, transaction, ",") } } called by: gawk -f script -v TRADERS=traders.txt orders.txt And there you have it. Some caveats: check the numbers, as implicit gawk number conversion might not be correct with zero-padded numbers. There is a fix for that in case; getline might explode if we run out of lines from traders. I haven't put any check, that's up to you no control over timestamps. Match is based on Order ID. Output file: Client ID,Order ID,Price,Volume,Turnover QOXA,QOXA6580494597,000001380,00001856,2561280 JBDX,JBDX6760353333,000000700,00002458,1720600 DOLR,DOLR9093170513,000000040,00003832,153280 QOXA,QOXA6563975285,000003470,00009428,32715160 QOXA,QOXA5569701531,000000750,00009023,6767250 YXKH,YXKH2329856527,000003830,00009947,38097010 QOXA,QOXA5832015401,000003990,00006432,25663680 QOXA,QOXA3909507967,000002690,00001847,4968430 DOLR,DOLR2262067037,000003470,00002732,9480040 YXKH,YXKH2637373848,000001090,00006105,6654450 DOLR,DOLR8639062962,000002750,00001975,5431250 JBDX,JBDX7268036859,000000520,00004986,2592720 JBDX,JBDX2840241688,000003790,00002029,7689910 YXKH,YXKH4095442568,000004640,00002150,9976000 JBDX,JBDX9945514579,000004080,00005904,24088320 QOXA,QOXA1900595629,000001520,00006866,10436320 JBDX,JBDX0126452783,000000170,00006615,1124550 XFKD,XFKD4392227425,000003550,00009948,35315400 XFKD,XFKD1759342196,000002570,00007816,20087120 JBDX,JBDX8177118129,000002240,00000427,956480 YXKH,YXKH6485420018,000003900,00008327,32475300 JBDX,JBDX1612537068,000001370,00001422,1948140 JBDX,JBDX2409813753,000001660,00003588,5956080 XFKD,XFKD4861992028,000001760,00004552,8011520 JBDX,JBDX0262502677,000000790,00003235,2555650 JBDX,JBDX2587184235,000002430,00006723,16336890 XFKD,XFKD6374702721,000004640,00009451,43852640 JBDX,JBDX6885176695,000001090,00009258,10091220 JBDX,JBDX5424320405,000000540,00003618,1953720 DOLR,DOLR8212353717,000004110,00003633,14931630 QOXA,QOXA5011416607,000001880,00007376,13866880 QOXA,QOXA7608439570,000001300,00007463,9701900 YXKH,YXKH7747780955,000003700,00006357,23520900 QOXA,QOXA2477859437,000003930,00009840,38671200 XFKD,XFKD4995735686,000004550,00009858,44853900 JBDX,JBDX3564523161,000002130,00000639,1361070 YXKH,YXKH7095815077,000002300,00003945,9073500 YXKH,YXKH5848658513,000004270,00002084,8898680 XFKD,XFKD5919379575,000002220,00003603,7998660 XFKD,XFKD8175209012,000003330,00006350,21145500 XFKD,XFKD8139382011,000003450,00007461,25740450 QOXA,QOXA8227838196,000001160,00007081,8213960 QOXA,QOXA2072297264,000001700,00004429,7529300 XFKD,XFKD6434550362,000003000,00002409,7227000 QOXA,QOXA9672544909,000003960,00001033,4090680 QOXA,QOXA6105975858,000003480,00008373,29138040 JBDX,JBDX1316207043,000002650,00005237,13878050 YXKH,YXKH6010226371,000001140,00004945,5637300 DOLR,DOLR1387517499,000004620,00006129,28315980 YXKH,YXKH3727923537,000003740,00008061,30148140 XFKD,XFKD5727938304,000004870,00007298,35541260 1: requires gawk 4.1.0 or higher
BASH - Extract Data from String
I have a log that returns thousands of lines of data, I want to extract a few values from that. In the log there is only one line containing the unquie unit reference so I can grep for that using: grep "unit=Central-C152" logfile.txt That produces a line of output similar to the following: a3cd23e,85d58f5,53f534abef7e7,unit=Central-C152,locale=32325687-8595-9856-1236-12546975,11="School",1="Mr Green",2="Qual",3="SWE",8="report",5="channel",7="reset",6="velum" The format of the line may change in that the order of the values won't always be in the same position. I'm trying to work out how to get the value of 2 and 7 in to separate variables. I had thought about cut on , or = but as the values aren't in a set order I couldn't work out that best way to do it. I' trying to get: var state=value of 2 without quotes var mode=value of 7 without quotes Can anyone advise on the best way to do this ? Thanks
Could you please try following to create variable's values. state=$(awk '/unit=Central-C152/ && match($0,/2=\"[^"]*/){print substr($0,RSTART+3,RLENGTH-3)}' Input_file) mode=$(awk '/unit=Central-C152/ && match($0,/7=\"[^"]*/){print substr($0,RSTART+3,RLENGTH-3)}' Input_file) You could print them too by doing following. echo "$state" echo "$mode" Explanation: Adding explanation of command too now. awk ' ##Starting awk program here. /unit=Central-C152/ && match($0,/2=\"[^"]*/){ ##Checking condition if a line has string (unit=Central-C152) and using match using REGEX to check from 2 to till " print substr($0,RSTART+3,RLENGTH-3) ##Printing substring starting from RSTART+3 till RLENGTH-3 characters. } ' Input_file ##Mentioning Input_file name here.
You are probably better off doing all of the processing in Awk. awk -F, '/unit=Central-C152/ { for(i=1;i<=NF;++i) if($i ~ /^[27]="/) { b[++k] = $i sub(/^[27]="/, "", b[k]) sub(/"$/, "", b[k]) gsub(/\\/, "", b[k]) } print "state " b[1] ", mode " b[2] }' logfile.txt This presupposes that the fields always occur in the same order (2 before 7). Maybe you need to change or disable the gsub to remove backslashes in the values. If you want to do more than print the values, refactoring whatever Bash code you have into Awk is often a better approach than doing this processing in Bash.
Assuming you already have the line in a variable such as with: line="$(grep 'unit=Central-C152' logfile.txt | head -1)" You can then simply use the built-in parameter substitution features of bash: f2=${line#*2=\"} ; f2=${f2%%\"*} ; echo ${f2} f7=${line#*7=\"} ; f7=${f7%%\"*} ; echo ${f7} The first command on each line strips off the first part of the line up to and including the <field-number>=". The second command then strips everything off that beyond (and including) the first quote. The third, of course, simply echos the value. When I run those commands against your input line, I see: Qual reset which is, from what I can see, what you were after.
Print a file in multiple columns based on delimiter
This seems like a simple task, but using duckduckgo I wasn't able to find a way to properly do what I'm trying to. The main question is: How do I split the output of a command in linux or bash into multiple columns using a delimeter? I have a file that looks like this: (this is just a simplified example) ----------------------------------- Some data that varies in line length ----------------------------------- ----------------------------------- More data that is seperated by a new line and dashes ----------------------------------- And so on. Everytime data gets written to the file, it's enclosed in a line of dashes, seperated by an empty line from the last block. Line-length of the data varies. What I want is basically a tool or way using bash to split the file into multiple columns like this: ----------------------------------- ----------------------------------- Some data More data that is seperated that varies in line length by a new line and dashes ----------------------------------- ----------------------------------- Each column should take 50% of the screen, no centering (as in alignment) needed. The file has to be split per-block. Splitting the file in the middle or something like that won't work. I basically want block 1 go to the left column, block 2 to the right, 3 to the left again, 4 right, and so on. The file gets updated constantly and updates should be written to the screen right away. (Currently I'm using tail -f) Since this sounds like a rather common question I would welcome a general approach to this instead of a specific answer that works only for my case so people coming from search engines looking for a way to have a two column layout in bash get some information too. I tried column and pr, both don't work as desired. (I elaborated on this in the comments) Edit: To be clear, I am looking for a general approach on this. Going through a file, getting data between the delimiter, putting it to column A, getting the next one putting it to column B, and so on.
The question is tagged as Perl so here is a possible Perl answer: #!/usr/bin/env perl use strict; use warnings; my $is_col1 = 1; my $in_block = 0; my #col1; while (<DATA>) { chomp; if (/^\s*-+\s*$/ ... /^\s*-+\s*$/) { $in_block = 1; if ($is_col1) { push #col1, $_; } else { printf "%-40s%-40s\n", shift #col1 // '', $_; } } else { if ($in_block) { $in_block = ! $in_block; $is_col1 = ! $is_col1; print "\n" if $is_col1; # line separating blocks } } } print join("\n", #col1), "\n\n" if #col1; __DATA__ ----------------------------------- Some data that varies in line length ----------------------------------- ----------------------------------- More data that is seperated by a new line and dashes with a longer column2 ----------------------------------- ----------------------------------- The odd last column ----------------------------------- Output: ----------------------------------- ----------------------------------- Some data More data that is seperated that varies in line length by a new line and dashes ----------------------------------- with a longer column2 ----------------------------------- ----------------------------------- The odd last column -----------------------------------
This script is getting max width of current terminal and splitting it in 2, then printing records split by RS="\n\n" separator, print the first found and placing the cursor at the first line/last column of it to write the next record. #!/bin/bash tput clear # get half current terminal width twidth=$(($(tput cols)/2)) tail -n 100 -f test.txt | stdbuf -i0 -o0 gawk -v twidth=$twidth 'BEGIN{ RS="\n\n"; FS=OFS="\n"; oldNF=0 } { sep="-----------------------------------" pad=" " printf "%-" twidth "s", $0 getline for(i = 1; i <= NF; i++){ # move cursor to first line, last column of previous record print "\033[" oldNF ";" twidth "f" $i oldNF+=1 } }' Here's a simpler version gawk 'BEGIN{ RS="[-]+\n\n"; FS="\n" } { sep="-----------------------------------" le=$2 lo=$3 getline printf "%-40s %-40s\n", sep,sep printf "%-40s %-40s\n", le,$2 printf "%-40s %-40s\n", lo,$3 printf "%-40s %-40s\n\n", sep,sep }' test.txt Output ----------------------------------- ----------------------------------- Some data More data that is seperated that varies in line length by a new line and dashes ----------------------------------- ----------------------------------- ----------------------------------- ----------------------------------- Some data More data that is seperated that varies in line length by a new line and dashes ----------------------------------- -----------------------------------
Assuming file contains uniform blocks of five lines each, using paste, sed, and printf: c=$((COLUMNS/2)) paste -d'#' <(sed -n 'p;n;p;n;p;n;p;n;p;n;n;n;n;n' file) \ <(sed -n 'n;n;n;n;n;p;n;p;n;p;n;p;n;p' file) | sed 's/.*/"&"/;s/#/" "/' | xargs -L 1 printf "%-${c}s %-${c}s\n" Problem with OP spec The OP reports that the block lengths may vary, and should be separated by a fixed number of lines. Even numbered blocks go in Column A, odd numbered blocks in Column B. That creates a tail -f problem then. Suppose the block lengths of the source input begin with 1000 lines, then one line, 1000, 1, 1000, 1, etc. So Column A gets all the 1000 line blocks, and Column B gets all the one line blocks. Let's say the blocks in the output are separated by 1 line each. So one block in Column A lines up with 500 blocks in Column B. So for a terminal with scrolling output, that means before we can output the first block in Column A, we have to wait for 1000 blocks of input. To output the third block in Column A, (just below the first block), we have to wait for 2000 blocks of input. If the blocks are added to the input file relatively slowly, with a one second delay between blocks, then it will take three seconds for the block 3 to appear in the input file, but it will take 33 minutes for block 3 to be displayed in the output file.
Alright, since apprently there is no clean way to do this I came up with my own solution. It's a bit messy and requires GNU screen to be installed, but it works. Any amount of lines within or around the blocks, 50% of the screen automatically resizing and each column prints independantly from each other with a fixed amount of newlines between them. Also automatic updates every x seconds. (120 in my example) #!/bin/bash screen -S testscr -X layout save default screen -S testscr -X split -v screen -S testscr -X screen tail -f /tmp/testscr1.txt screen -S testscr -X focus screen -S testscr -X screen tail -f /tmp/testscr2.txt while : ; do echo "" > /tmp/testscr1.txt echo "" > /tmp/testscr2.txt cfile=1 # current column ctype=0 # start or end of block while read; do if [[ $REPLY == "------------------------------------------------------------" ]]; then if [[ $ctype -eq 0 ]]; then ctype=1 else if [[ $cfile -eq 1 ]]; then echo "${REPLY}" >> /tmp/testscr1.txt echo "" >> /tmp/testscr1.txt echo "" >> /tmp/testscr1.txt cfile=2 else echo "${REPLY}" >> /tmp/testscr2.txt echo "" >> /tmp/testscr2.txt echo "" >> /tmp/testscr2.txt cfile=1 fi ctype=0 fi fi if [[ $ctype -eq 1 ]]; then if [[ $cfile -eq 1 ]]; then echo "${REPLY}" >> /tmp/testscr1.txt else echo "${REPLY}" >> /tmp/testscr2.txt fi fi done < "$1" sleep 120 done First, start a screen session with screen -S testscr then, either within or outside the session, execute the script above. This will split the screen vertically using 50% per column and execute tail -f on both columns, afterwards it will go through the input file and write block by block to each tmp. file in the desired way. Since it's in an infinite while loop it's essentially automatically updating the shown output every x seconds (here 120).
Shell Extract Text Before Digits in a String
I've found several examples of extractions before a single character and examples of extracting numbers, but I haven't found anything about extracting characters before numbers. My question: Some of the strings I have look like this: NUC320 Syllabus Template - 8wk SLA School Template - UL CJ101 Syllabus Template - 8wk TECH201 Syllabus Template - 8wk Test Clone ID17 In cases where the string doesn't contain the data I want, I need it to be skipped. The desired output would be: NUC-320 CJ-101 TECH-201 SLA School Template - UL & Test Clone ID17 would be skipped. I imagine the process being something to the effect of: Extract text before " " Condition - Check for digits in the string Extract text before digits and assign it to a variable x Extract digits and assign to a variable y Concatenate $x"-"$y and assign to another variable z More information: The strings are extracted from a line in a couple thousand text docs using a loop. They will be used to append to a hyperlink and rename a file during the loop. Edit: #!/bin/sh # my files are named 1.txt through 9999.txt i both # increments the loop and sets the filename to be searched i=1 while [ $i -lt 10000 ] do x=$(head -n 31 $i.txt | tail -1 | cut -c 7-) if [ ! -z "$x" -a "$x" != " " ]; then # I'd like to insert the hyperlink with the output on the # same line (1.txt;cj101 Syllabus Template - 8wk;www.link.com/cj101) echo "$i.txt;$x" >> syllabus.txt # else # rm $i.txt fi i=`expr $i + 1` sleep .1 done
sed for printing lines starting with capital letters followed by digits. It also adds a - between them: sed -n 's/^\([A-Z]\+\)\([0-9]\+\) .*/\1-\2/p' input Gives: NUC-320 CJ-101 TECH-201
A POSIX-compliant awk solution: awk '{ if (match($1, /[0-9]+$/)) print substr($1, 1, RSTART-1) "-" substr($1, RSTART) }' \ file | while IFS= read -r token; do # Process token here (append to hyperlink, ...) echo "[$token]" done awk is used to extract the reformatted tokens of interest, which are then processed in a shell while loop. match($1, /[0-9]+$/) matches the 1st whitespace-separated field ($1) against extended regex [0-9]+$, i.e., matches only if the fields ends in one or more digits. substr($1, 1, RSTART-1) "-" substr($1, RSTART) joins the part before the first digit with the run of digits using -, via the special RSTART variable, which indicates the 1-based character position where the most recent match() invocation matched.
awk '$1 ~/[0-9]/{sub(/...$/,"-&",$1);print $1}' file NUC-320 CJ-101 TECH-201