How to make a character line across the width of the command line Windows?
In Linux, I can do like this
printf '\n%*s\n\n' \"${COLUMNS:-$(tput cols)}\" '' | tr ' ' -
In Windows, I have so far only
echo -----------------------
you can get the current width of your window with the mode command.
#echo off
setlocal enabledelayedexpansion
"set width="
for /f "tokens=2 delims=:" %%a in ('mode con^|more +4') do if not defined width set /a width=%%a
for /l %%a in (1,1,%width%) do set "line=!line!-"
echo %line%
If you are on a supported version of Windows, this can easily be done using PowerShell. PowerShell also runs on Linux/*NIX and Mac.
powershell -NoLogo -NoProfile -Command "'-' * $Host.UI.RawUI.WindowSize.Width"
I only know a workaround for it by determining the width of the command line window and repeating the character ofter enough.
Since I don't know if you only want it in the command prompt oder in a batch file I post what I made while ago for me. It only works in a batch file or when you save the second part in a batch file and call it in a command prompt window.
:RepeatChar <Char> <Count> <Variable>
setlocal enabledelayedexpansion
set tempRepChar=
for /L %%l in (1,1,%~2) do (
set tempRepChar=!tempRepChar!%~1
)
if /i "%~3"=="" (
echo %tempRepChar%
) else (
set %~3=%tempRepChar%
set tempRepChar=
)
goto :EOF
exit /b
(the extra exit /b in the RepeatChar function isn't really necessary, but I just do it for myself)
You can then call it within a batch file with
for /f %%f in ('powershell.exe -command $host.UI.RawUI.WindowSize.Width') do set WindowsWidth=%%f
call :RepeatChar "-" %WindowsWidth% Stipline
echo %Stripline%
exit /b
if you don't give it the 3rd parameter then it just echos the line so if you only need it once you can just use
call :RepeatChar "-" %WindowsWidth%
Or you can also store or use it via a for loop, like
for /f %%f in ('powershell.exe -command $host.UI.RawUI.WindowSize.Width') do set WindowsWidth=%f
for /f %%f in ('call temp.bat "-" "%WindowsWidth%"') do (
echo %%f
set Stripline=%%f
)
I wrote a small batch code which uses command FORFILES for looping through directories. OS is Windows 10 Pro x64 and the batch file is executed with cmd.exe.
The code is:
setLocal EnableDelayedExpansion
#echo off
SET LogFile=C:\logs
SET Days=1
SET Source=\\SERVER\SHARE\Folder
PUSHD %Source%
FOR /f %%a IN ('type %LogFile%\dirs.log') DO (
MKDIR "%LogFile%\%%a" 2> NUL
DEL %LogFile%\%%a\archive.log /Q 2>NUL
ECHO Collecting %%a....
FORFILES /P %%a /D -%Days% /C "cmd /C ECHO #RELPATH >> %LogFile%\%%a\archive.log" 2> NUL
)
POPD
The batch file reads the folders to be scanned from a text file with command FOR. This text file is created by following batch code:
dir.log creating batch code:
PUSHD %Source%
FOR /f %%a IN ('cd') DO SET CurPath=%%a
FOR /f %%a IN ('dir /B /S /AD') DO (
SET Directory=%%a
SET Directory=!Directory:%CurPath%\=!
ECHO !Directory! >> %LogFile%\dirs.log
)
POPD
The file dirs.log contains folder list with the following folder structure:
Folder1
Folder2
Folder1\Subfolder1
Folder1\Subfolder2
Folder2\Subfolder1
This text file contains only directory paths line by line with no space or any other character with special meaning for Windows command interpreter. So there is no problem caused by not using "delims=" in FOR loop of first posted batch file.
The command FORFILES is executed for each directory path from text file with appropriate parameters.
The batch file outputs the loop variable a of FOR. But on each batch file execution additional empty lines are randomly output between the lines output with ECHO Collecting %%a.....
FORFILES puts empty lines to console randomly when it is running.
What is causing this?
This batch code creates the file dirs.log and runs FORFILES with redirecting the empty line output by FORFILES itself before running cmd.exe to device NUL to suppress it. A possible error output of FORFILES is also suppressed by redirecting it from STDERR to STDOUT being redirected to device NUL. Read the Microsoft article about Using Command Redirection Operators for details.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LogFilesPath=C:\logs"
set "Days=1"
set "Source=\\SERVER\SHARE\Folder"
pushd "%Source%"
if not "%CD:~-1%" == "\" ( set "CurPath=%CD%" ) else set "CurPath=%CD:~0,-1%"
del "%LogFilesPath%\dirs.log" 2>nul
setlocal EnableDelayedExpansion
rem WARNING: Directory paths with one or more exclamation marks are
rem not correct processed by this batch code because of
rem enabled delayed environment variable expansion.
for /F "delims=" %%I in ('dir /B /S /AD 2^>nul') do (
set "Directory=%%I"
set "Directory=!Directory:%CurPath%\=!"
echo !Directory!>>"%LogFilesPath%\dirs.log"
)
endlocal
if not exist "%LogFilesPath%\dirs.log" goto ExitBatch
for /F "usebackq delims=" %%I in ("%LogFilesPath%\dirs.log") do (
mkdir "%LogFilesPath%\%%I" 2>nul
del "%LogFilesPath%\%%I\archive.log" 2>nul
echo Collecting %%I ...
%SystemRoot%\System32\forfiles.exe /P "%CurPath%\%%I" /D -%Days% /C "%SystemRoot%\System32\cmd.exe /C echo #RELPATH>>"%LogFilesPath%\%%I\archive.log"" >nul 2>&1
)
:ExitBatch
popd
endlocal
Some notes about this code:
The help of command SET output on running set /? in a command prompt window lists on last help page the dynamic environment variable CD for current directory path. What is not explained is that %CD% expands to current directory path with no backslash at end with the exception on current directory is the root directory of a drive. The first FOR command line in dirs.log creating code can be safely replaced by using %CD% with the IF condition which is faster than the FOR loop running cmd.exe in background to execute cd and capture its output.
The DIR command line is executed by FOR in a background command process started with cmd.exe /C. DIR perhaps finds no directory in which case a confusing error message is output to STDERR. That is the reason for using 2>nul on DIR command line. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
There is no space between echo !Directory! and >>"%LogFilesPath%\dirs.log" because this space would be also written into the log file as trailing space which is not wanted here and would be bad for remaining code.
FOR can read a text file itself line by line with skipping empty lines and skipping with default options also lines starting with a semicolon. The option usebackq is used to get the log file name enclosed in double quotes interpreted as file name and not as string to process. The option delims= defines an empty list of delimiters which disables line splitting done by default on spaces/tabs. Run in a command prompt window for /? for help on this command.
I'm writing a batch script that will use a WMIC command to get a list of all groups on a Windows machine, get the group info by using net localgroup <groupname>, and then write the info to an output file. Here is what I have:
for /f "skip=1" %%a in ('"wmic group get name"') do net localgroup %%a >> "%OUTPUTFILEPATH%" 2> nul && echo. >> "%OUTPUTFILEPATH%" && echo ^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^=^= >> "%OUTPUTFILEPATH%" && echo. >> "%OUTPUTFILEPATH%"
The issue I am having seems to be with getting quotes around the %%a variable in do net localgroup %%a because it outputs the info for groups like Administrators just fine but when it gets to a group name like Remote Desktop Users, it fails to return the info for the group.
In other words, this is what seems to be happening in the FOR loop:
net localgroup Administrators
net localgroup Remote Desktop Administrators
The first operation is successful. The second is not. Obviously, there need to be quotes around Remote Desktop Administrators in order for it to be seen as a single argument but I can't quite figure out how to get this to happen for my %%a FOR loop variable.
I have tried putting quotes around "%%a" and '%%a'. This doesn't help.
Any input would be greatly appreciated.
REM
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "OUTPUTFILEPATH=u:\ofp.txt"
DEL /F /Q "%outputfilepath%"
for /f "skip=1delims=" %%a in ('wmic group get name') do (
SET "group=%%a"
CALL :loptrail
IF DEFINED group (
net localgroup "!group!" >> "%OUTPUTFILEPATH%" 2> NUL
echo.>> "%OUTPUTFILEPATH%"
echo ==========================================>> "%OUTPUTFILEPATH%"
echo.>> "%OUTPUTFILEPATH%"
)
)
GOTO :EOF
:loptrail
SET "group=%group:~0,-1%"
IF "%group:~-1%"==" " GOTO loptrail
GOTO :eof
The issue is that wmic output is unicode and %%a requires delims= else it returns the first [implicit-space]-delimited token.
Unfortunately, this means that %%a contains trailing spaces, which net appears to disapprove of.
Since batch does not allow a metavariable to be substringed, you need to put it into a common environment variable and invoke enabledelayedexpansion.
The value in group appears to be terminated by an extra NULL, so the :loptrail routine first arbitrarily removes that last character, then any remaining trailing spaces.
The very last line of wmic output will generate a forlorn empty group so the net processing proceeds only if group is non-empty.
You need to use either tokens=* as suggested by Carlos Gutiérrez or even better delims= on FOR to avoid splitting up the line into tokens based on spaces and tabs.
It is of course additionally necessary to enclose the group name in double quotes when any group name contains 1 or more spaces.
But the main problem here is that wmic outputs information in Unicode with UTF-16 Little Endian encoding which command FOR cannot parse right directly. A workaround for this issue is redirecting output of wmic into a temporary text file and using command type to get contents in ASCII/ANSI/OEM, i.e. one byte per character. See the answers on How to correct variable overwriting misbehavior when parsing output for details.
But there is one more problem here as wmic outputs the group names with trailing spaces. Those trailing spaces must be additionally removed before group name is passed as parameter to console application net enclosed in double quotes.
#echo off
setlocal EnableDelayedExpansion
set "OutputFile=%USERPROFILE%\Desktop\LocalGroupInfo.txt"
del "%OutputFile%" 2>nul
%SystemRoot%\System32\wbem\wmic.exe group get name >"%Temp%\%~n0.tmp"
for /f "skip=1 delims=" %%a in ('type "%Temp%\%~n0.tmp"') do (
set "GroupName=%%a"
call :RemoveTrailingSpaces
%SystemRoot%\System32\net.exe localgroup "!GroupName!" >>"%OutputFile%" 2>nul
if not errorlevel 1 (
echo.>>"%OutputFile%"
echo ==========================================>>"%OutputFile%"
echo.>>"%OutputFile%"
)
)
del "%Temp%\%~n0.tmp"
endlocal
:RemoveTrailingSpaces
if not "%GroupName:~-1%" == " " exit /B
set "GroupName=%GroupName:~0,-1%"
goto RemoveTrailingSpaces
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
del /?
echo /?
endlocal /?
exit /?
for /?
goto /?
if /?
net localgroup /?
set /?
setlocal /?
type /?
wmic group get /?
Try:
for /f "tokens=*" %%a in ('"wmic group get name"') do ....
I am trying to convert a long filename to a short filename (8.3) on Windows.
A batch-file with a command line argument works as intended:
short.bat:
#echo OFF
echo %~s1
calling short.bat C:\Documents and Settings\User\NTUSER.DAT returns C:\DOCUM~1\USER\NTUSER.DAT
However, I don't like having an extra .bat-file for this. I would rather call cmd.exe with the whole command from a ruby script.
How can I do this?
As an intermediate step I tried to hardcode the path in the batch-file, but that does not work:
short1.bat:
#echo OFF
SET filename="C:\Documents and Settings\User\NTUSER.DAT"
echo %filename%
echo %~sfilename%
echo %filename% works, but echo %~sfilename% gives the following error:
The following usage of the path operator in batch-parameter
substitution is invalid: %~sfilename%
For valid formats type CALL /? or FOR /?
If short1.bat works, how can I convert this into a one-liner that can be called with cmd.exe \c ...?
There is another question (how to get DOS path instead of Windows path), however that one is specifically asking for the path of the current directory.
cmd /c for %A in ("C:\Documents and Settings\User\NTUSER.DAT") do #echo %~sA
Replace the filename.txt to the filename you want to convert to 8.3
dir /x filename.txt
You will then have to split the result with whitespace as your delimiter (\s in regex).
Then the value with the ~ is your short filename. If your filename is short to begin with, then you won't find a string containing a ~.
Here is an example that read in the registry the location of your "appdata\local" folder and convert it to short path:
cls
#echo off
cd /d "%~dp0"
chcp 65001 >nul
for /f "skip=1" %%a in ('"wmic useraccount where name='%USERNAME%' get sid"') do (
for %%b in (%%a) do set current_SID=%%b
)
set current_username=%USERNAME%
set current_userprofile=%USERPROFILE%
set key_to_read=HKEY_USERS\%current_SID%\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders
set value_to_read=Local AppData
rem If value_to_read contains ? space(s) set tokens to 2+?
for /f "usebackq eol= tokens=3,* delims= " %%a in (`reg query "%key_to_read%" /v "%value_to_read%" 2^>nul ^| find "%value_to_read%"`) do (
set value_type=%%a
set data_read=%%b
)
set data_read=%data_read:USERPROFILE=current_userprofile%
call set "data_read=%data_read%"
set current_local_appdata=%data_read%
set current_local_appdata_temp=%current_local_appdata%\Temp
echo %current_local_appdata_temp%
for %%a in ("%current_local_appdata_temp%") do set "current_local_appdata_temp_short=%%~sa"
echo %current_local_appdata_temp_short%
pause
exit
I would like to know how to loop through each line in a text file using a Windows batch file and process each line of text in succession.
I needed to process the entire line as a whole. Here is what I found to work.
for /F "tokens=*" %%A in (myfile.txt) do [process] %%A
The tokens keyword with an asterisk (*) will pull all text for the entire line. If you don't put in the asterisk it will only pull the first word on the line. I assume it has to do with spaces.
For Command on TechNet
If there are spaces in your file path, you need to use usebackq. For example.
for /F "usebackq tokens=*" %%A in ("my file.txt") do [process] %%A
From the Windows command line reference:
To parse a file, ignoring commented lines, type:
for /F "eol=; tokens=2,3* delims=," %i in (myfile.txt) do #echo %i %j %k
This command parses each line in Myfile.txt, ignoring lines that begin with a semicolon and passing the second and third token from each line to the FOR body (tokens are delimited by commas or spaces). The body of the FOR statement references %i to get the second token, %j to get the third token, and %k to get all of the remaining tokens.
If the file names that you supply contain spaces, use quotation marks around the text (for example, "File Name"). To use quotation marks, you must use usebackq. Otherwise, the quotation marks are interpreted as defining a literal string to parse.
By the way, you can find the command-line help file on most Windows systems at:
"C:\WINDOWS\Help\ntcmds.chm"
In a Batch File you MUST use %% instead of % : (Type help for)
for /F "tokens=1,2,3" %%i in (myfile.txt) do call :process %%i %%j %%k
goto thenextstep
:process
set VAR1=%1
set VAR2=%2
set VAR3=%3
COMMANDS TO PROCESS INFORMATION
goto :EOF
What this does:
The "do call :process %%i %%j %%k" at the end of the for command passes the information acquired in the for command from myfile.txt to the "process" 'subroutine'.
When you're using the for command in a batch program, you need to use double % signs for the variables.
The following lines pass those variables from the for command to the process 'sub routine' and allow you to process this information.
set VAR1=%1
set VAR2=%2
set VAR3=%3
I have some pretty advanced uses of this exact setup that I would be willing to share if further examples are needed. Add in your EOL or Delims as needed of course.
Improving the first "FOR /F.." answer:
What I had to do was to call execute every script listed in MyList.txt, so it worked for me:
for /F "tokens=*" %A in (MyList.txt) do CALL %A ARG1
--OR, if you wish to do it over the multiple line:
for /F "tokens=*" %A in (MuList.txt) do (
ECHO Processing %A....
CALL %A ARG1
)
Edit: The example given above is for executing FOR loop from command-prompt; from a batch-script, an extra % needs to be added, as shown below:
---START of MyScript.bat---
#echo off
for /F "tokens=*" %%A in ( MyList.TXT) do (
ECHO Processing %%A....
CALL %%A ARG1
)
#echo on
;---END of MyScript.bat---
#MrKraus's answer is instructive. Further, let me add that if you want to load a file located in the same directory as the batch file, prefix the file name with %~dp0. Here is an example:
cd /d %~dp0
for /F "tokens=*" %%A in (myfile.txt) do [process] %%A
NB:: If your file name or directory (e.g. myfile.txt in the above example) has a space (e.g. 'my file.txt' or 'c:\Program Files'), use:
for /F "tokens=*" %%A in ('type "my file.txt"') do [process] %%A
, with the type keyword calling the type program, which displays the contents of a text file. If you don't want to suffer the overhead of calling the type command you should change the directory to the text file's directory. Note that type is still required for file names with spaces.
I hope this helps someone!
The accepted answer is good, but has two limitations.
It drops empty lines and lines beginning with ;
To read lines of any content, you need the delayed expansion toggling technic.
#echo off
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ text.txt"`) do (
set "var=%%a"
SETLOCAL EnableDelayedExpansion
set "var=!var:*:=!"
echo(!var!
ENDLOCAL
)
Findstr is used to prefix each line with the line number and a colon, so empty lines aren't empty anymore.
DelayedExpansion needs to be disabled, when accessing the %%a parameter, else exclamation marks ! and carets ^ will be lost, as they have special meanings in that mode.
But to remove the line number from the line, the delayed expansion needs to be enabled.
set "var=!var:*:=!" removes all up to the first colon (using delims=: would remove also all colons at the beginning of a line, not only the one from findstr).
The endlocal disables the delayed expansion again for the next line.
The only limitation is now the line length limit of ~8191, but there seems no way to overcome this.
Or, you may exclude the options in quotes:
FOR /F %%i IN (myfile.txt) DO ECHO %%i
Here's a bat file I wrote to execute all SQL scripts in a folder:
REM ******************************************************************
REM Runs all *.sql scripts sorted by filename in the current folder.
REM To use integrated auth change -U <user> -P <password> to -E
REM ******************************************************************
dir /B /O:n *.sql > RunSqlScripts.tmp
for /F %%A in (RunSqlScripts.tmp) do osql -S (local) -d DEFAULT_DATABASE_NAME -U USERNAME_GOES_HERE -P PASSWORD_GOES_HERE -i %%A
del RunSqlScripts.tmp
If you have an NT-family Windows (one with cmd.exe as the shell), try the FOR /F command.
The accepted anwser using cmd.exe and
for /F "tokens=*" %F in (file.txt) do whatever "%F" ...
works only for "normal" files. It fails miserably with huge files.
For big files, you may need to use Powershell and something like this:
[IO.File]::ReadLines("file.txt") | ForEach-Object { whatever "$_" }
or if you have enough memory:
foreach($line in [System.IO.File]::ReadLines("file.txt")) { whatever "$line" }
This worked for me with a 250 MB file containing over 2 million lines, where the for /F ... command got stuck after a few thousand lines.
For the differences between foreach and ForEach-Object, see Getting to Know ForEach and ForEach-Object.
(credits: Read file line by line in PowerShell )
Modded examples here to list our Rails apps on Heroku - thanks!
cmd /C "heroku list > heroku_apps.txt"
find /v "=" heroku_apps.txt | find /v ".TXT" | findstr /r /v /c:"^$" > heroku_apps_list.txt
for /F "tokens=1" %%i in (heroku_apps_list.txt) do heroku run bundle show rails --app %%i
Full code here.
To print all lines in text file from command line (with delayedExpansion):
set input="path/to/file.txt"
for /f "tokens=* delims=[" %i in ('type "%input%" ^| find /v /n ""') do (
set a=%i
set a=!a:*]=]!
echo:!a:~1!)
Works with leading whitespace, blank lines, whitespace lines.
Tested on Win 10 CMD