Removing some columns and rows from csv file via batch - batch-file
I am trying to create a batch file that will edit a .csv and remove the first column, and any summary lines contained in the file. I am, however, fairly new to programming batch files, so I am not sure the best way to start this, and it would be great if you could include a basic explanation of how the code works so I can be self-sustaining in the future!
,Type,Date,Num,Name,Memo,Member,Clr,Split,Alias,Value,Balance
ABB - Egypt,,,,,,,,,,,
ElAin EL-Sokhna,,,,,,,,,,,
,Invoice,09-06-10,12005,ABB - EL-Sokhna,,Accounts Receivable,,Training Income,15000,,15000
,Invoice,09-14-11,12005,ABB - EL-Sokhna,“ElAin EL-Sokhna“ Trainer for OTS Application: First two weeks,Training Income,,Accounts,,150001,0
Total ElAin EL-Sokhna,,,,,,,,,241194,210400,301794
ABB - Egypt - Other,,,,,,,,,,,
There are various iterations of this file, as they come from a monthly report, I need to remove the first (empty) column, and any rows that look like ABB - Egypt,,,,,,,,,,, or Total ElAin EL-Sokhna,,,,,,,,,241194,210400,301794
So the output should be:
Type,Date,Num,Name,Memo,Member,Clr,Split,Alias,Value,Balance
Invoice,09-06-10,12005,ABB - EL-Sokhna,,Accounts,,Training Income,15000,,15000
Invoice,09-14-11,13002,ABB - EL-Sokhna,“ElAin EL-Sokhna“ Trainer for OTS Application: First two weeks,Training Income,,Accounts,,150001,0
Thanks for the input!
EDIT: It seems I wasn't clear enough in my OP (Sorry, first time here).
There are two processes that need to happen here, in every file the first column must be deleted, and any lines that are either title lines ABB - Egypt,,,,,,,,,,, or summary lines Total ElAin EL-Sokhna,,,,,,,,,241194,210400,301794 need to be removed.
All lines that need to be kept will be mostly filled in, such as ,Type,Date,Num,Name,Memo,Member,Clr,Split,Alias,Value,Balance or ,Invoice,09-06-10,12005,ABB - EL-Sokhna,,Accounts Receivable,,Training Income,15000,,15000 Notice that, as in the second line, it is possible for there to be some missing values in them, so doing a search for something like ",," will not work.
Batch is a terrible language for modifying text files. There are a great many special cases that require arcane knowledge to work around the problem. You may have a script that seems to do what you want, and then some wrinkle appears in your data, and the entire script may have to be redesigned.
With regard to your specific problem, it appears to me that you only want to preserve rows that begin with a comma, meaning the first column is empty. Of those remaining rows, you want to remove the first (empty) column.
Assuming none of the rows you want to keep have an empty value for the second column, then there is a really trivial solution:
#echo off
>"%~1.new" (for /f "delims=, tokens=*" %%A in ('findstr "^," %1') do echo %%A)
move /y "%~1.new" %1 >nul
The script expects the file to be passed as the first and only argument. So if your script is named "fixCSV.bat", and the file to be modified is "c:\test\file.csv", then you would use:
fixCSV "c:\test\file.csv"
The %1 expands to the value of the first argument, and %~1 is the same, except it also strips any enclosing quotes that may or may not be present.
The FINDSTR command reads the file and writes out only lines that begin with a comma. The FOR /F command iterates each line of output. The "delims=, tokens=*" options effectively strip all leading commas from each line, and the result is in variable %%A, which is then ECHOed. The entire construct is enclosed in parentheses and stdout is redirected to a temporary file. Finally, the temporary file is moved over top of the original file, thus replacing it.
If the 2nd column may be empty, then the result will be corrupted because it removes all leading commas (both columns 1 and 2 in this case). The script must be more complicated to compensate. You would need to set a variable and then use delayed expansion to get the sub-string, skipping the first character. But delayed expansion will corrupt expansion of the %%A variable if it contains the ! character. So delayed expansion must be toggled on and off. You are beginning to see what I mean by lots of special cases.
#echo off
setlocal disableDelayedExpansion
>"%~1.new" (
for /f "delims=" %%A in ('findstr "^," %1') do (
set "ln=%%A"
setlocal enableDelayedExpansion
echo !ln:~1!
endlocal
)
)
move /y "%~1.new" %1 >nul
As the batch scripts become more complicated, they become slower and slower. It may not be an issue for most files, but if the file is really large (say hundreds of megabytes) then it can become an issue.
I almost never use pure batch to modify text files anymore. Instead, I use a hybrid JScript/batch utility that I wrote called JREPL.BAT. The utility is pure script that runs natively on any Windows machine from XP onward. JREPL.BAT is able to efficiently modify text files using regular expression replacement. Regular expressions can appear to be mysterious, but they are well worth the investment in learning.
Assuming you have JREPL.BAT somewhere within your PATH, then the following command is all that you would need:
jrepl "^,(.*)" "$1" /jmatch /f "yourFile.csv" /o -
The /F option specifies the file to read.
The /O option with value of - specifies that the output should replace the original file.
The /JMATCH option specifies that each replacement value is written out to a new line. All other text is dropped.
The first argument is the search expression. It matches any line that begins with a comma, and everything after that is captured in a variable named $1.
The second argument specifies the replacement value, which is simply the captured value in variable $1.
A way will be to define all your rules in a variable which will be used against
findstr. The rules must be defined like this :
/c:"String which exclude the line" /c:"Another string which exclude the Line" /c: "etc.."
This rules must be exact (That they can't be found in a line who must stay).
For the empty first colonne you can use a substitution the way i made it in the code with
,Type=Type
,Invoice=Invoice
Test.bat :
#echo off&cls
setlocal enabledelayedexpansion
Rem The rules
set $String_To_Search=/c:"ABB - Egypt," /c:"Total ElAin El-Sokhna," /c:"ElAin EL-Sokhna," /c:"ABB - Egypt - Other,"
for /f "delims=" %%a in (test.csv) do (
set $line=%%a
Rem the substitutions for the first Column
set $Line=!$Line:,Type=Type!
set $line=!$Line:,Invoice=Invoice!
Rem the test and the ouput if nothing was found
echo !$Line! | findstr /i %$String_To_Search% >nul || echo !$Line!
))>Output.csv
I used a file test.csv for my test.
The ouput is redirected to Output.csv
Perhaps is this what you want?
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%a in (input.csv) do (
set "line=%%a"
if "!line:~0,1!" equ "," echo !line:~1!
)
When a problem is not enough explained we can only guess the missing details. In this case, I assumed that you just want the lines that start with comma, deleting it. The output is the same as your output example...
EDIT: Output example added
Type,Date,Num,Name,Memo,Member,Clr,Split,Alias,Value,Balance
Invoice,09-06-10,12005,ABB - EL-Sokhna,,Accounts Receivable,,Training Income,15000,,15000
Invoice,09-14-11,12005,ABB - EL-Sokhna,“ElAin EL-Sokhna“ Trainer for OTS Application: First two weeks,Training Income,,Accounts,,150001,0
I would start here to learn this: How can you find and replace text in a file using the Windows command-line environment?
It covers many details of substitution from Windows command line and many ways to do it, some requiring only what's built into Windows, and some requiring other downloadable software.
Magoo is right, more criteria is needed, but there might be enough information in the linked page for you to get past the main hurdles.
#ECHO OFF
SETLOCAL
(FOR /f "tokens=*delims=," %%a IN ('findstr /b /l "," q28079306.txt') DO ECHO %%a)>newfile.txt
GOTO :EOF
I used a file named q28079306.txt containing your data for my testing.
Produces newfile.txt
Related
Batch loopvariable manipulation
I have a small problem with a .bat file that I have to build to manipulate a specific .csv. I want the .bat to read the line of the file, and then check for the first three letters of that line. At the end there should be n-files where file xxx.csv contains the lines of the input.csv with xxx as the first three letters of line. First things first, I don't even know if it is possible to do it this job in a batch-file, because the file has >85000 lines and may even get much bigger. So if it is impossible you can directly tell me that. for /f "delims=" %%a in (input.CSV) DO ( echo %%~a:~0,3 pause ) I want to "output" the first three letters of %%a. It would be great if you could help me. Phil
Substring substitution only works with environment variables (%var%), but not with metavariables (%%a) (as Mofi already commented). And because you are setting and using a variable within the same command block, you need delayed expansion: setlocal enabledelayedexpansion for /f "delims=" %%a in (input.CSV) DO ( set "var=%%~a" echo !var:~0,3! pause ) (there are methods without delayed expansion, but they make use of call, which slows things down)
BATCH file to copy specific text and past after X in multiple .txt files
I started learning Batch files commands and I succeeded to create some basic scripts to simplify some tasks at work. Now I'm looking to automate a repetitive task that takes a lot of time to be done... and to say I'm doing it manually each day : I have a bunch of .txt files grouped in the same folder and the content of these texte files is like below : Comments lines START (name of electronic component) (Number of pins) Program body line 1 Program body line 2 . Program body line X END Comments lines What I'm doing is to copy the "name of the electronic part" that I'm working on and paste it after END. Here is an example : START BC547 3 Program body line 1 Program body line 2 . Program body line X END BC547 There are numerous blank END in the same file, you can imagine filling 200 to 300 text files every day manually ... In some cases the structure changes to : Comments lines START (name1) (Number of pins) Comments lines START (subcircuit1) (Number of pins) Program body line 1 . Program body line X END (subcircuit1) Comments lines START (subcircuit2) (Number of pins) Program body line 1 . Program body line X END (subcircuit2) Comments lines START (subcircuitx) (Number of pins) Program body line 1 . Program body line X END (subcircuitx) Comments lines END (name1) Comments lines I would be much thankful if someone can make batch code to copy the full next word after START and paste it in the next END below. The script have to be able to detetct the second case when STAR syntaxes are consecutive. Thank you in advance for your help!
The community has determined that open ended questions asking for de novo code to meet a set of business requirements is out of scope for StackOverflow batchfile questions. But I am bored and couldn't help myself. The algorithm is fairly strait forward. Iterate all the lines of the source file. If the line begins with START, then parse out the ID and push the value on a stack (array). If the line begins with END then append the last stack value to the line and pop the stack. If the line does not begin with END then simply write the original line. But nothing in batch is simple. FOR /F disregards lines that are empty, so FINDSTR /N is used to prefix each line with a line number, followed by a colon. String manipulation is performed within the loop to strip off the line number prefix. Batch doesn't have formal support for arrays, but the code shows how to emulate arrays. Delayed expansion is required within a parenthesized block of code, so an array member may not be accessed as !ID.%i%. The code shows how to transfer the current i value to a FOR variable so you can use !ID.%%I instead. Delayed expansion is toggled ON and OFF within the loop to protect any ! that may be present within the source file. You cannot write to the file you are reading from. So the result must be written to a temporary new file, which is later MOVEd to replace the original. The script below should be called with one or more file masks that specify which files should be processed. If the script is named "fixEnd.bat", then fixEnd test.txt would process test.txt in the current directory. fixEnd "c:\somePath\*.txt" would process all .txt files within the "c:\somePath" folder. fixEnd file1.txt file2.txt would process those two files in the current directory. #echo off setlocal disableDelayedExpansion for %%F in (%*) do ( set /a i=0 set "ID.0=" >"%%F.new" ( for /f "delims=" %%L in ('findstr /n "^" "test.txt"') do for /f "tokens=1,2,3 delims=: " %%A in ("%%L") do ( if "%%B" == "START" ( set /a i+=1 setlocal enableDelayedExpansion for %%I in (!i!) do ( endlocal set "ID.%%I=%%C" ) ) if "%%B" == "END" ( setlocal enableDelayedExpansion for %%I in (!i!) do ( (echo END !ID.%%I!) endlocal set "ID.%%I=" set /a "1/i, i-=1)" 2>nul %= division by zero error prevents negative i values =% ) ) else ( set "ln=%%L" setlocal enableDelayedExpansion (echo(!ln:*:=!) endlocal ) ) ) move /y "%%~F.new" "%%F" >nul ) As much as I enjoy the challenge of working with batch, I long ago came to the conclusion that it is not practical to use pure batch to manipulate text files except for really simple cases. And in many cases batch simply is not up to the task. Which is why I wrote JREPL.BAT, a regular expression text processing utility that is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward. JREPL has myriad options that give it tremendous power, especially the ability to incorporate user supplied JScript on the command line. Using JREPL, the same algorithm is implemented in a much more straightforward way, and the code is much faster than pure batch: #echo off for %%F in (%*) do for %%F in (test.txt) do call jrepl^ "^START\b\s*(\S*)/^END\b.*"^ "id[id.length]=$2;$txt=$0/$txt='END '+(id.length?id[id.length-1]:'');id=id.slice(0,-1)"^ /jbeg "var i=0, id=[]" /t "/" /jq /f "%%F" /o - Use jrepl /?? from the command line to view the entire documentation, one screen at a time. jrepl /?help lists all available types of help. jrepl /?options gives a brief summary of all available options. jrepl /?/t would show the help for the /T option. Etc.
Batch script to read a tab separated text file line by line and extract one column to a new file
I have text files from exporting iTunes playlists that are tab separated arrays. They have 27 columns, but for simplicity, I'll just include four in the example: Name Artist My Rating Location Pod1 Show1 0 E:\Podcasts\Show1\Episode99.m4a Pod2 Show2 100 E:\Podcasts\Show2\Show2 Ep 101 - Subtitle.mp3 Pod3 Show2 60 E:\Podcasts\Show2\News 11-17-2014.aicc And so on. I need to extract only the Location (which is always the last column) and place it in a new text file so it looks like this: E:\Podcasts\Show1\Episode99.m4a E:\Podcasts\Show2\Show2 Ep 101 - Subtitle.mp3 E:\Podcasts\Show2\News 11-17-2014.aicc Here's the code I have so far towards that goal: #ECHO OFF type Podcasts.txt | findstr /v Grouping > Podcasts1.txt del Podcasts.txt rename Podcasts1.txt Podcasts.txt While I've found some separate tutorials on modifying single lines and reading text files line by line, I've been having difficulty even modifying a single line the way I'd like, let alone merging the two principles. I figure this is probably a very quick answer I'm just not making the right search to find. I found that it's possible to get the results I want in Notepad++ using the following REGEX: Find: .+(E\:) Replace: \1 So a way to do that line by line in a batch script would be helpful. Bonus: I'm using the resulting file as a whitelist for deleting files that aren't on the playlist. If you have a link or can write a quick bit of code, that helps. Otherwise, I have ideas on how I plan to accomplish this that would require using a single folder. EDIT: I've discovered that because some columns are blank, using this: for /f "usebackq tokens=1-26,* delims= " %%a in ("Podcasts.txt") do ( echo %%q >> PodcastsTest.txt ) Will sometimes hit the wrong column. I need something that can either search for the two characters "E:" and act from there or for the line search to go right to left in finding the first tab. The quick and dirty method I just created that searches for a backslash (which currently only appears in Location) is the following: for /f "usebackq tokens=1,* delims=\" %%a in ("Podcasts.txt") do ( echo E:\%%b >> PodcastsTest.txt ) This is not a secure method since there's nothing stopping some other fields from having backslashes, but as a temporary solution, it works. I discovered that Linux SED is available for Windows. Maybe there is a way to adapt the following Notepad++ REGEX search for it? Find: .+(E\:) Replace: \1
This is a new solution based on limitations of the source data and extra information added in the question, as well as acceptance to use another tool: call jrepl ".*(E:.*)" "$1" /i /f "Podcasts.txt" /o "Podcasts2.txt" The code above uses a native Windows batch script called Jrepl.bat (by dbenham) jrepl.bat can be downloaded from: https://www.dropbox.com/s/4otci4d4s8x5ni4/Jrepl.bat and it can also be found here: http://www.dostips.com/forum/viewtopic.php?f=3&t=6044 Place it in the same folder as the batch file, or in a folder that is on the system path. Part 2 The code below can then be used to generate a file which lists all the files in the current folder, that are not in the file generated from the jrepl code above. You mention single folder - so that's what this handles and the file can be examined before using it to nuke the files. In fact this can be used with the original source file - if the filenames only appear once inside the file. #echo off ( for %%a in (*.*) do ( if /i not "%%~nxa"=="%~nx0" if /i not "%%~nxa"=="Podcasts2.txt" find /i "\%%~nxa" < "Podcasts2.txt" >nul || echo del "%%a" ) )>delfiles.bat.txt" Below is a basic solution with data in all cells in the tab delimited file Without the file I can't be sure it has the right column but the technique should work. Replace TAB with a literal tab character in an editor that doesn't convert TABs to spaces, like Notepad. #echo off for /f "usebackq tokens=1-26,* delims=TAB" %%a in ("Podcasts.txt") do ( echo %%b ) pause
Here is a pure batch solution that relies on the supposition that the desired location always begins with E:\ (not case sensitive), and that string must not occur in any earlier columns: #echo off setlocal disableDelayedExpansion >"Podcasts.txt.new" ( for /f "usebackq skip=1 delims=" %%A in ("Podcasts.txt") do ( set "ln=%%A" setlocal enableDelayedExpansion echo !ln:*E:\=E:\! endlocal ) ) If you know that no location contains !, then you can simply enable delayed expansion at the top once, and remove the SETLOCAL and ENDLOCAL from the loop. A regular expression is a great way to get the last column, regardless of content. A good regular expression utility for batch is JREPL.BAT - a pure script based utility (hybrid JScript/batch) that runs natively on any Windows machine from XP onward. The following JREPL one liner works well: jrepl "[^\t]+$" $0 /jmatch /jbegln "skip=(ln==1)" /f "Podcasts.txt" /o "Podcasts.txt.new" Here is another JREPL solution: jrepl ".*\t" "" /a /jbegln "skip=(ln==1)" /f "Podcasts.txt" /o "Podcasts.txt.new" Note - you must use CALL JREPL if you use either JREPL solution within a batch script. That last regex can be adopted for use with sed: sed "1d;s/.*\t//" "Podcasts.txt" >"Podcasts.txt.new"
with sed and several assumption PathTocygwin/sed -e '\#.*[[:blank:]]\([A-Z]:/.*$# !d' -e 's//\1/' Podcasts.txt > Podcasts1.txt del Podcasts.txt rename Podcasts1.txt Podcasts.txt assumption here - path is using a mapping drive - there is no previous path in 1 to 25 column - keep only line that correspond to the pattern
Batch script to grab lines with findstr without filepath
I've got a log file that monitors a large system including requests and acknowledgements. My objective is to be able to: 1. Loop through the script and get the lines where requests & their acknowledgements happen 2. Pull the entire lines of importance as strings and store them as variables for string modifying to output somewhere else. Here's what I have so far: #ECHO off setlocal setlocal enabledelayedexpansion setlocal enableextensions :: Lets get today's date, formatted the way the ABCD File is named for /f "tokens=1-5 delims=/ " %%d in ("%date%") do set targetDate=%%f-%%d-%%e :: Now we set the targetFile name SET ABCDLogsFile=C:\Users\me\Documents\monitoring_file_for_jim\ABCDFIX*%targetDate%.log ::****Scrapped original approach***** set "ackFoundCount=0" set "reqFoundCount=0" ::Get lines with acks for /f delims^=^ eol^= %%a in ('findstr /c:"\<ACK\>" "%ABCDLogsFile%"') do ( set /a "ackFoundCount+=1" setlocal enabledelayedexpansion for %%N in (!ackFoundCount!) do ( endlocal set "ackFound%%N=%%a" ) ) ::Get lines with requests for /f delims^=^ eol^= %%b in ('findstr /c:"ReqSingle" "%ABCDLogsFile%"') do ( set /a "reqFoundCount+=1" setlocal enabledelayedexpansion for %%N in (!reqFoundCount!) do ( endlocal set "reqFound%%N=%%b" ) ) setlocal enabledelayedexpansion for /l %%N in (1,1,2 %reqFoundCount%) do echo REQ %%N FOUND= !reqFound%%N! pause for /l %%N in (1,1,2 %ackFoundCount%) do echo ACK %%N FOUND= !ackfound%%N! endlocal EDIT 2 dbenham The roundabout way I was trying to accomplish this before was totally unnecessary. Thanks to the questions and answer here: 'findstr' with multiple search results (BATCH) I've got my script working similarly. However, I'm curious if its possible to get findstr output without the filepath at the beginning. I only need to substring out the timestamp in the log, which would always be the first 12 characters of each line (without the filepath). My output currently is prefixed with the path, and while I could get the path where the log would eventually be in production, it would be safer to try and do it another way. At the time that this script would eventually be run, there would only be 1 or 2 reqs and acks each, that is why I store all which are found. It's not necessary but I think it would be reassuring to see two if there are two. Here is what the output looks like for acks and reqs alike: C:\Users\me\Documents\monitoring_file_for_jim\ABCDFIX 2015-04-01.log:2015-03-26 07:00:11,028 INFO etc... I'm thinking that if I could strip the filepath off the start, then all I'd need to do to get just the timestamps of the events would be for /l %%N in (1,1,1 %reqFoundCount%) do echo Req %%N occurred at: !reqFound%%N:~0,12! >> MorningAckChecks.txt for /l %%N in (1,1,1 %ackFoundCount%) do echo ACK %%N occurred at: !ackfound%%N:~0,12! >> MorningAckChecks.txt
I suspect you could not get SKIP to work because you you were iterating the delimited list of line numbers with a FOR statement, which means the number is in a FOR variable. Problem is, you cannot include FOR variables or (delayed expansion) when specifying a SKIP value, or any other FOR option. The batch parser evaluates the FOR options before FOR variables are expanded, so it couldn't possibly work. Only normal expansion can be used when including a variable as part of FOR options. But I don't understand why you think you need the line numbers at all. FINDSTR is already able to parse out the lines you want. Simply use FOR /F to iterate each matching line. For each line, define a variable containing the line content, and then use substring operations to parse out your desired values. But I can offer an alternative that I think could make your life much easier. JREPL.BAT is a sophisticated regular expression text processor that could identify the lines and parse out and transform your desired values, all in one pass. JREPL.BAT is a hybrid JScript/batch script that runs natively on any Windows machine from XP onward. If I knew what your input looked like, and what your desired output is, then I could probably knock up a simple solution using JREPL.BAT. Or you could read the extensive built in documentation and figure it out for yourself. Documentation is accessed from the command line via jrepl /?. You might want to pipe the output through MORE so you get one screen of help at a time. But I never do because my command line console is configured with a large output buffer, so I can simply scroll up to see past output. EDIT - In response to comment and updated question Here are the relevant snippets of your code that are causing the problem. SET ABCDLogsFile=C:\Users\me\Documents\monitoring_file_for_jim\ABCDFIX*%targetDate%.log findstr /c:"\<ACK\>" "%ABCDLogsFile%" findstr /c:"ReqSingle" "%ABCDLogsFile% The issue is your ABCDLogsFile definition includes a wildcard, which causes FINDSTR to prefix each matching line with the full path to the file name where the match occurred. I have a simple solution for you - Just change the definition of ABCDLogsFile as follows: SET "ABCDLogsFile=C:\Users\me\Documents\monitoring_file_for_jim\ABCDFIX<%targetDate%.log" Explanation My solution relies on two undocumented features 1) Undocumented file mask wildcards. < - Very similar to * > - Very similar to ? These symbols are normally used for redirection, so they must be either quoted or escaped if you want to use them as file mask wildcards. We discuss the undocumented feature at DosTips - Dir undocumented wildcards. Sprinkled throughout the thread (and a link) are some example use cases. I document my understanding of exactly how the non-standard wildcards work at http://www.dostips.com/forum/viewtopic.php?p=39420#p39420 2) FINDSTR works with the non-standard wildcards FINDSTR will prefix each matching line with the file name (and possibly path) if any of the following conditions occur The /M option is used The /F option is used Multiple input files are explicitly listed on the command line Multiple input files are implied via a file mask with at least one * or ? wildcard on the command line Your are getting the file path prefix because of the last trigger - the * in your file mask. But you can use < instead to get the same result, except the non-standard wildcards do not trigger the file prefix in the output. Problem solved :-) I talk about this FINDSTR feature at http://www.dostips.com/forum/viewtopic.php?p=39464#p39464. Some day I hope to update my What are the undocumented features and limitations of the Windows FINDSTR command? post with this tasty little tidbit.
This post has become a bit cluttered. It would be very helpful if you posted the lines of input that correspond to the output you are getting. If you can't do that then add this statement before your FOR. I am sure you will find that testReqSkip is blank. echo.testReqSkip=%testReqSkip%
Windows Batch help in setting a variable from command output [duplicate]
This question already has answers here: Set output of a command as a variable (with pipes) [duplicate] (6 answers) Closed 7 years ago. I need to run a simple find command and redirect the output to a variable in a Windows Batch File. I have tried this: set file=ls|find ".txt" echo %file% But it does not work. If I run this command it works without problems: set file=test.txt echo %file% So obviously my command output is not being set to my variable. Can anyone help? Thanks
I just find out how to use commands with pipes in it, here's my command (that extracts the head revision of an svn repo) : SET SVN_INFO_CMD=svn info http://mySvnRepo/MyProjects FOR /f "tokens=1 delims=" %%i IN ('%SVN_INFO_CMD% ^| find "Revision"') DO echo %%i
First of all, what you seem to expect from your question isn't even possible in UNIX shells. How should the shell know that ls|find foo is a command and test.txt is not? What to execute here? That's why UNIX shells have the backtick for such things. Anyway, I digress. You can't set environment variables to multi-line strings from the shell. So we now have a problem because the output of ls wouldn't quite fit. What you really want here, though, is a list of all text files, right? Depending on what you need it's very easy to do. The main part in all of these examples is the for loop, iterating over a set of files. If you just need to do an action for every text file: for %%i in (*.txt) do echo Doing something with "%%i" This even works for file names with spaces and it won't erroneously catch files that just have a .txt in the middle of their name, such as foo.txt.bar. Just to point out that your approach isn't as pretty as you'd like it to be. Anyway, if you want a list of files you can use a little trick to create arrays, or something like that: setlocal enabledelayedexpansion set N=0 for %%i in (*.txt) do ( set Files[!N!]=%%i set /a N+=1 ) After this you will have a number of environment variables, named Files[0], Files[1], etc. each one containing a single file name. You can loop over that with for /l %%x in (1,1,%N%) do echo.!Files[%%x]! (Note that we output a superfluous new line here, we could remove that but takes one more line of code :-)) Then you can build a really long line of file names, if you wish. You might recognize the pattern: setlocal enabledelayedexpansion set Files= for %%i in (*.txt) do set Files=!Files! "%%i" Now we have a really long line with file names. Use it for whatever you wish. This is sometimes handy for passing a bunch of files to another program. Keep in mind though, that the maximum line length for batch files is around 8190 characters. So that puts a limit on the number of things you can have in a single line. And yes, enumerating a whole bunch of files in a single line might overflow here. Back to the original point, that batch files have no way of capturing a command output. Others have noted it before. You can use for /f for this purpose: for /f %%i in ('dir /b') do ... This will iterate over the lines returned by the command, tokenizing them along the way. Not quite as handy maybe as backticks but close enough and sufficient for most puposes. By default the tokens are broken up at whitespace, so if you got a file name "Foo bar" then suddenly you would have only "Foo" in %%i and "bar" in %%j. It can be confusing and such things are the main reason why you don't ever want to use for /f just to get a file listing. You can also use backticks instead of apostrophes if that clashes with some program arguments: for /f "usebackq" %%i in (`echo I can write 'apostrophes'`) do ... Note that this also tokenizes. There are some more options you can give. They are detailed in the help for command.
set command has /p option that tells it to read a value from standard input. Unfortunately, it does not support piping into it, but it supports reading a value from a first line of existing file. So, to set your variable to the name of a first *.txt file, you could do the following: dir /b *.txt > filename.tmp set /p file=< filename.tmp del /q filename.tmp It is important not to add a space before or even after =. P. S. No fors, no tokens.
Here's a batch file which will return the last item output by find: #echo off ls | find ".txt" > %temp%\temp.txt for /f %%i in (%temp%\temp.txt) do set file=%%i del %temp%\temp.txt echo %file% for has a syntax for parsing command output, for /f "usebackq", but it cannot handle pipes in the command, so I've redirected output to a temporary location. I strongly recommend, given that you have access to ls, that you consider using a better batch language, such as bash or even an scripting language like python or ruby. Even bash would be a 20x improvement over cmd scripting.
The short answer is: Don't! A windows shell env var can hold a max of 32 Kb and it isn't safe to save output from programs in them. That's why you can't. In batch script you must adopt another programming style. If you need all of the output from the program then save it to file. If you only need to check for certain properties then pipe the output into a program that does the checking and use the errorlevel mechanism: #echo off type somefile.txt | find "somestring" >nul if %errorlevel% EQU 1 echo Sorry, not found! REM Alternatively: if errorlevel 1 echo Sorry, not found! However, it's more elegant to use the logical operators Perl style: #echo off (type somefile.txt | find "somestring" >nul) || echo Sorry, not found!
It's not available in DOS, but in the Windows console, there is the for command. Just type 'help for' at a command prompt to see all of the options. To set a single variable you can use this: for /f %%i in ('find .txt') do set file=%%i Note this will only work for the first line returned from 'find .txt' because windows only expands variable once by default. You'll have to enable delayed expansion as shown here.
what you are essentially doing is listing out .txt files. With that, you can use a for loop to over dir cmd eg for /f "tokens=*" %%i in ('dir /b *.txt') do set file=%%i or if you prefer using your ls, there's no need to pipe to find. for /f "tokens=*" %%i in ('ls *.txt') do set file=%%i
Example of setting a variable from command output: FOR /F "usebackq" %%Z IN ( `C:\cygwin\bin\cygpath "C:\scripts\sample.sh"` ) DO SET BASH_SCRIPT=%%Z c:\cygwin\bin\bash -c '. ~/.bashrc ; %BASH_SCRIPT%' Also, note that if you want to test out the FOR command in a DOS shell, then you need only use %Z instead of %%Z, otherwise it will complain with the following error: %%Z was unexpected at this time.