2011-11-04 61 views
8

Tôi đang cố gắng viết một kịch bản lệnh có được (trong số những thứ khác) một danh sách tất cả các ổ đĩa mà máy tính có. Mã cơ bản trông giống như sau:Tại sao vòng lặp FOR/f trong tập lệnh batch này đánh giá một dòng trống?

REM Build the list of disk drives to monitor 
SETLOCAL enabledelayedexpansion 
FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
    SET "DISK_DATABASES=!DISK_DATABASES!%%a|" 
    SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\\|" 
) 

Tôi khá rõ ràng là xây dựng hai danh sách có định dạng hơi khác nhau để sử dụng sau này. Khi tôi chạy này, tuy nhiên, sản lượng tôi nhận được một cái gì đó trông như thế này:

C|D|E|| 
C:\\|D:\\|E:\\|:\\| 

Bây giờ, tôi hy vọng các ống dấu trong cả hai trường hợp và tôi có thể quản lý đó, nhưng tôi thực sự bối rối vì sao có thêm mục nhập trống trong đó. Nếu tôi chạy lệnh wmic theo cách thủ công, tôi có thể thấy rằng thực sự có một dòng trống ở cuối đầu ra, nhưng sự hiểu biết của tôi là /f đặc biệt phải bỏ qua các dòng trống.

Nếu tôi bật ECHO vào, có vẻ như dòng cuối cùng đó chỉ đến như một dòng trả lại/dòng mới hoặc tương tự. Có cách nào để làm những gì tôi mong đợi không? Tui bỏ lỡ điều gì vậy? Tôi đã cố gắng để viết một điều kiện if trong vòng lặp để loại trừ dòng cuối cùng này, nhưng nó đã được ... sôi nổi và không bao giờ làm việc. Tôi đánh giá cao bất kỳ/tất cả trợ giúp.

Trả lời

5

Trong trường hợp này lặp đi lặp lại cuối cùng không tạo ra một mục rỗng, và bạn nhận ra bạn của C|D|E|| chỉ với echo %DISK_DATABASES%,
nhưng echo !DISK_DATABASES! chí đầu ra ||D|E| ??

Đó là vì phần tử cuối cùng là một ký tự <CR>.
<CR> ký tự được xóa trực tiếp sau khi mở rộng phần trăm, nhưng không bị trễ mở rộng.

Bạn có thể tránh điều này, sử dụng việc mở rộng phần trăm để loại bỏ chúng

setlocal EnableDelayedExpansion 
FOR /f "skip=1 tokens=1 delims=:" %%a in ('"WMIC logicaldisk WHERE drivetype=3 GET deviceid"') do (
    set "item=%%a" 
    call :removeCR 

    if not "!item!"=="" (
    SET "DISK_DATABASES=!DISK_DATABASES!!item!|" 
    SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!!item!:\\|" 
) 
) 
goto :eof 
:removeCR 

:removeCR 
set "Item=%Item%" 
exit /b 
+0

Wow, chúng ta đang sống và học tập, chúng ta không? Tôi tự hỏi tại sao một tiện ích như WMIC lại tạo ra một ký tự ' 'duy nhất trên một dòng đầu tiên ... Hoặc có thể đó là kết quả của những gì @matt đã phát hiện ra. –

+0

Tôi cho rằng nó không phải là vấn đề unicode, như ngay cả trong XP Microsoft did't biết làm thế nào kết thúc dòng riêng của họ nên được. IPconfig (XP) cũng tạo ra các kết thúc dòng bất thường – jeb

+0

Nhờ cả jeb và matt cho các câu trả lời. Cả hai giải pháp đều hoạt động, nhưng jeb's không yêu cầu tôi tạo các tệp tạm thời để tôi chấp nhận nó. – Morinar

4

Theo http://ss64.com/nt/for_f.html

Nhiều người trong số các lệnh mới và các tiện ích (ví dụ WMIC) tập tin văn bản đầu ra ở định dạng unicode, chúng có thể không được đọc bởi các lệnh FOR mà hy vọng ASCII. Để chuyển đổi định dạng tệp, hãy sử dụng lệnh TYPE.

Vì vậy, dường như WMIC và FOR không phát cùng nhau tốt đẹp.

3

tôi phát hiện ra một phương pháp hiệu quả hơn và đáng tin cậy hơn tước không mong muốn <CR> từ ngày kết thúc mỗi dòng. Không có tệp tạm thời và không cần CALL.

Tôi không hiểu cơ chế của cách FOR/F chuyển đổi đầu ra unicode WMIC thành ASCII. Thông thường FOR/F không thể đọc unicode. Tuy nhiên, nó hoạt động, mỗi dòng được chuyển đổi kết thúc bằng <CR><CR><LF>. FOR/F phân tách các dòng tại mỗi <LF> và sau đó nếu ký tự cuối cùng trong dòng là <CR>, nó sẽ nằm trong số <CR> cuối cùng, trong trường hợp này là để lại số <CR> không mong muốn.

Giải pháp là chỉ cần vượt qua mỗi dòng thông qua một trong nhiều FOR/F :-)

@echo off 
setlocal enableDelayedExpansion 
for /f "skip=1 delims=" %%A in (
    'wmic logicaldisk where "drivetype=3" get deviceid' 
) do for /f "tokens=1 delims=:" %%B in ("%%A") do (
    set "disk_databases=!disk_databases!%%B|" 
    set "drives_to_monitor=!drives_to_monitor!%%B:\\|" 
) 

Phương pháp này là đáng tin cậy hơn sau đó sử dụng mở rộng bình thường bởi vì bạn không cần phải lo lắng về việc trích dẫn hoặc thoát đặc biệt nhân vật. Ví dụ, phương pháp CALL sử dụng mở rộng bình thường không thể xử lý một chuỗi như "this & that" & the other. Nhưng phương pháp này không có vấn đề với một chuỗi như vậy.

+0

Ý tưởng hay sử dụng 'for/f' cho điều này, thông thường tôi thấy hành vi này gây phiền nhiễu, nhưng trong trường hợp này thì nó hữu ích. – jeb

0

thành ngữ tiêu chuẩn Mỹ để đối phó với điều này là để viết ra từ WMIC vào một tập tin tạm thời, sau đó sử dụng TYPE (làm giảm UTF16 để ASCII) để thức ăn đó vào CHO, như thế này:

:: Standard environment setup 
setlocal enabledelayedexpansion 
:: Every variable whose name starts with "tf" will identify a temporary 
:: file - remove any such variables inherited from the parent environment 
for /f %%V in ('set tf') do set %%V= 
:: Create some temporary filenames. Prefix all of them with this script's 
:: own name to avoid clashes with those owned by other scripts. 
for /l %%I in (1,1,4) set tf%%I="%temp%\%~n0-temp%%I.txt" 

:: Use temp file to work around coding mismatch between WMIC out and FOR in 
wmic product where "name like 'Microsoft Office %% 2010'" get packagecache >!tf1! 
for /f "skip=1" %%P in ('type !tf1!') do if exist "%%~P" msiexec /x "%%~P" /passive /norestart 

:: Before quitting script, clean up temporary files 
for /f %%V in ('set tf') do if exist "%%~V" del /f /q "%%~V" 
endlocal 
0

Run lệnh sau:

wmic blah /value | find "=" >> wherever 

Output sẽ là:

field=value 

Lưu ý rằng sẽ không có thêm dòng.

11

Tôi vừa mới xem chủ đề này. Tôi đã sử dụng findstr/v loại trừ dòng trống:

FOR /f "usebackq skip=1 tokens=1 delims=:" %%a in (`WMIC logicaldisk WHERE "drivetype=3" GET deviceid ^| findstr /v /r "^$"`) do (
+0

Đây là câu trả lời hay hơn nhiều IMO, không yêu cầu Mở rộng bị trễ,: CALL hoặc được lồng vào vòng lặp. Dễ dàng và đơn giản! – wpg4665

+0

thực sự tốt hơn nhiều, thx dmitry – Deus777

0

Thêm ^| findstr . và bạn sẽ chỉ nhận được không dòng trống

 
    REM Build the list of disk drives to monitor 
    SETLOCAL enabledelayedexpansion 
    FOR /f "skip=1 tokens=1 delims=:" %%a in (
    '"WMIC logicaldisk WHERE drivetype=3 GET deviceid" ^| findstr .') do (
     SET "DISK_DATABASES=!DISK_DATABASES!%%a|" 
     SET "DRIVES_TO_MONITOR=!DRIVES_TO_MONITOR!%%a:\|" 
    ) 

Các vấn đề liên quan